The aim of this notebook is to demonstrate how to use the Seurat R package to process scRNA-seq data.

More on basic analysis with Seurat in https://satijalab.org/seurat/articles/pbmc3k_tutorial.html.

See also https://github.com/quadbiolab/scRNAseq_analysis_vignette/blob/master/Tutorial.md for a more in-depth tutorial.

General setup

Load necessary libraries

Note, tutorial with seurat V5, which is substantially different from earlier version. https://satijalab.org/seurat/articles/announcements.html

The download of version 5 or above should happen automatically. If you have worked with previous versions, download v5 or above following https://satijalab.org/seurat/articles/install.html

# Bioconductor package installer
if(!require("BiocManager", quietly = TRUE))
  install.packages("BiocManager")
Bioconductor version 3.18 (BiocManager 1.30.22), R 4.3.1 (2023-06-16)
Bioconductor version '3.18' is out-of-date; the current release version '3.21' is available with R
  version '4.5'; see https://bioconductor.org/install
packages_use = c("Seurat", # package with conveniently organised scRNA-seq methods
                 "clustree", # a method to help choose clustering resolution
                 "dplyr",  # important to work with some data structures in R
                 "ggplot2", # plotting
                 "patchwork", # assembling plots
                 "cowplot", # assembling plots
                 "gplots") # also plotting)


# for each package listed: if not installed, install, otherwise load it
for(p in packages_use){
  if(!(p %in% rownames(installed.packages()))){
    BiocManager::install(p)
    library(p, character.only = T)
  } else{
    library(p, character.only = T)
  }
}

Attaching package: ‘cowplot’

The following object is masked from ‘package:patchwork’:

    align_plots


Attaching package: ‘gplots’

The following object is masked from ‘package:stats’:

    lowess
# set seed for randomisation (e.g. UMAP dimension reduction)
set.seed(123)

Load data

Load your dataset. For this tutorial, we will use a dataset of 1k Peripheral blood mononuclear cells (PBMCs) from a healthy donor from 10x Genomics. https://www.10xgenomics.com/datasets/1-k-pbm-cs-from-a-healthy-donor-v-3-chemistry-3-standard-3-0-0

To read the data, we will use a dedicated function that reads cellranger outputs. In this case, we will read the filtered matrix.

The Read10X() function reads in the output of the cellranger pipeline from 10X, returning a unique molecular identified (UMI) count matrix. The values in this matrix represent the number of molecules for each feature (i.e. gene; row) that are detected in each cell (column). Note that more recent versions of cellranger now also output using the h5 file format, which can be read in using the Read10X_h5() function in Seurat.

PBMC_data = Read10X(data.dir = "/Users/miguel/Documents/scRNAseq course/2025/filtered_feature_bc_matrix_1k/")

This imports the matrix of counts into a objects of class dgCMatrix. Lets examine a few genes in the first thirty cells

PBMC_data[c("CD3D", "TCL1A", "MS4A1"), 1:30]
3 x 30 sparse Matrix of class "dgCMatrix"
  [[ suppressing 30 column names ‘AAACCCAAGGAGAGTA-1’, ‘AAACGCTTCAGCCCAG-1’, ‘AAAGAACAGACGACTG-1’ ... ]]
                                                                   
CD3D  . . 6 3 6  . . . . 3 4 . 9 . . . . . 21 . 3 . 4 . 2 2 6 . . .
TCL1A . 8 . . .  . 7 . . . . . . . . . . .  . 2 . . . . . . . . . .
MS4A1 . 4 . . . 16 1 . . . . . . . 4 . . .  . 3 . . . . . . . 7 . .

The . values in the matrix represent 0s (no molecules detected). Since most values in an scRNA-seq matrix are 0, Seurat uses a sparse-matrix representation whenever possible. This results in significant memory and speed savings for Drop-seq/inDrop/10x data.

We can use these to make a Seurat object, which is what we will use in the analysis:

srat <- CreateSeuratObject(counts = PBMC_data, project = "PBMC_10x")

Basic Seurat object structure

There are two important components of the Seurat object to be aware of:

The @meta.data slot, which stores metadata for our droplets/cells (e.g. which batch of samples they belong to, total counts, total number of detected genes, etc.).

The @assays slot, which stores the matrix of raw counts, as well as (further down) matrices of normalised/transformed data.

Starting with our metadata slot:

head(srat@meta.data)

The rownames of this table represent the droplet/cell ids, given in the rownames of this table. The “orig.ident” has the sample names we specified earlier. And we can see that Seurat automatically calculated total UMI counts (nCount_RNA) and the total number of detected genes (nFeature_RNA) in each droplet. The meta.data contains information for each cell on key measurements (number of UMI, number of genes, ..), experimental variables (batch, condition, …), or output labels (clusters)

We can also look at cell and gene names by accessing the colnames and rownames, respectivelly.

head(colnames(srat))
[1] "AAACCCAAGGAGAGTA-1" "AAACGCTTCAGCCCAG-1" "AAAGAACAGACGACTG-1" "AAAGAACCAATGGCAG-1"
[5] "AAAGAACGTCTGCAAT-1" "AAAGGATAGTAGACAT-1"
head(rownames(srat))
[1] "MIR1302-2HG" "FAM138A"     "OR4F5"       "AL627309.1"  "AL627309.3"  "AL627309.2" 

Note: the cell names are the ROWNAMES of the @meta.data slot, but the COLNAMES of the Seurat object

Moving on to the assays slot, we can see that it currently contains just the count data, called $RNA. Normalised data of different sorts will also get stored here.

srat@assays
$RNA
Assay (v5) data with 33538 features for 1222 cells
First 10 features:
 MIR1302-2HG, FAM138A, OR4F5, AL627309.1, AL627309.3, AL627309.2, AL627309.4, AL732372.1,
OR4F29, AC114498.1 
Layers:
 counts 

Being aware of the active assay is important when doing different types of analysis because tools will try to use the active assay by default if they can. This will make more sense as we proceed with the downstream analysis. You will also have more than one assay in the case of multimodal data: you could have an assay for RNA and another for ATAC or protein data (CITE-Seq). You can check the active assay and modify it by running the following commands:

srat@active.assay
[1] "RNA"
DefaultAssay(srat) <- 'RNA'

Quality control

The main quality control metrics for scRNA-seq data are: - nCount_RNA: cells with very low counts are likely not very informative, or may be dead cells. However, some cell types (e.g. platelets, sperm) are smaller than usual, and thus are expected to have very few counts (<500). Knowing your biological system is important!

Get % of reads coming from MT transcripts. In most species, these can be found either by their gene name (starting with “mt-”) or by finding if the gene is encoded in the mitochondrial chromosome (retrieving that information from, for example, the Ensembl BioMart). In the human genome, all mitochondrial genes start with MT. This is probably not the case for your non-human species of interest, although for mouse it is usually ‘Mt’.

srat = PercentageFeatureSet(srat, col.name = "percent.mt", assay = "RNA", pattern = "^MT-") 

head(srat@meta.data)

Another simple metric that we can quickly calculate is the number of genes detected in the whole dataset (i.e. those that are true for this statement):

table(rowSums(srat@assays$RNA) > 0)
Warning: data layer is not found and counts layer is used

FALSE  TRUE 
15147 18391 

We can use the VlnPlot() function to visualise data stored in our metadata slot. This function will generate violin plots with samples on the x-axis and the variable of interest on the y-axis. We will use these metrics to select which cells (dots) stay within the main range for these QC metrics.

VlnPlot(srat, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)
Warning: Default search for "data" layer in "RNA" assay yielded no results; utilizing "counts" layer instead.

VlnPlot(srat, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3, pt.size = 0)
Warning: Default search for "data" layer in "RNA" assay yielded no results; utilizing "counts" layer instead.

VlnPlot(srat, features = c("nFeature_RNA", "nCount_RNA"), ncol = 2, log = T) # these two metrics are also useful in log scale
Warning: Default search for "data" layer in "RNA" assay yielded no results; utilizing "counts" layer instead.

And as scatterplots. This works on small samples because there are not that many points.

Examining the relationship between number of Counts/UMI and Features also shows us how cells can have the same sequencing depth (UMI) but express more/fewer genes (i.e. a biological quantity)

plot1 = FeatureScatter(srat, feature1 = "nCount_RNA", feature2 = "percent.mt")
plot2 = FeatureScatter(srat, feature1 = "nCount_RNA", feature2 = "nFeature_RNA")
plot1 + plot2

Those cells with high percent.mt also tend to have low read counts, extra evidence that these are low quality cells which will not be useful for our analysis.

Read count is well correlated with feature count. This is what we would expect. If there were cells with lots of reads but few features this would suggest problems with the capture of diverse RNA molecules in those cells.

Filtering cells

The simplest way to filter out cells is with hard cutoffs across all samples

Based on this exploration, we will pick some thresholds to remove the most outlying droplets: with greater than 500 and less than 4000 detected features, and greater than 3000 and less than 30000 UMI counts. This excludes poor quality cells with little data and potential doublets which have more features than we expect to see in the cells. We will also exclude droplets with less than 20% mitochondrial reads.

We might want to come back and adjust these cut offs once we have seen the UMAP plots. It may be that we still get clusters of low quality cells that should have been removed. Better to be cautious to begin with, to avoid filtering out unusual cell types.

# define condition for filtering
cells_to_filter <- rownames(subset(srat, subset = nFeature_RNA > 500 & nFeature_RNA < 4000 & nCount_RNA > 3000 & nCount_RNA < 30000 &  percent.mt < 20)@meta.data)
# add this information to the metadata
srat$keep <- rownames(srat@meta.data) %in% cells_to_filter

## quickly address how many cells we are keeping
table(srat$keep)

FALSE  TRUE 
  192  1030 

Let’s look back at our distribution plots, highlighting the cells that we are retaining and filtering.

VlnPlot(srat, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), log=TRUE, group.by="keep")
Warning: Default search for "data" layer in "RNA" assay yielded no results; utilizing "counts" layer instead.

Once we are happy with the filtering, we can remove the low quality cells from the Seurat object. Note that features here are genes and samples are cells.

srat_filt <- subset(srat, subset = keep)
srat_filt
An object of class Seurat 
33538 features across 1030 samples within 1 assay 
Active assay: RNA (33538 features, 0 variable features)
 1 layer present: counts
## other option for filtering based on cell names
#srat_filt = subset(srat, cells = cells_to_filter)

You should save the data at regular intervals (but you don’t have to right now)

#saveRDS(srat_filt, file = "Example_filt_srat.RDS")

Normalisation

Log transform

Normalise expression of each gene in each cell by the total counts in that cell, with a scaling factor of 10000, and then log-transform the data. This is done to somewhat control expression in each cell for their sequencing depth (although usually it’s not sufficient), as well as have the data adopt a more Gaussian distribution (since counts are Negative Binomial/Poisson distributed), to make it more amenable to methods like PCA.

DefaultAssay(srat_filt) = "RNA"
srat_filt = NormalizeData(srat_filt, normalization.method = "LogNormalize", scale.factor = 10000)
Normalizing layer: counts
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|

Then, we can find the highly variable genes. These are genes that are the most different between cells, and thus more likely to be important to reflect the differences between them. Using HVG can help to highlight cell populations by focusing on the most different genes, as well as reduce some computation times since fewer genes are being considered. However, doing this filtering is not always necessary.

There are many parameters to set as thresholds for this. We can decide on what thresholds to use to define their variability, expression level, etc.; or on how many genes we’d like to get (e.g. top 5000).

# top 5000
srat_filt = FindVariableFeatures(srat_filt, selection.method = "vst", 
                                 nfeatures = 5000)
Finding variable features for layer counts
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
# Identify the 20 most highly variable genes
top20 = head(VariableFeatures(srat_filt), 20)

# plot variable features with and without labels
plot1 = VariableFeaturePlot(srat_filt)
plot2 = LabelPoints(plot = plot1, points = top20, repel = TRUE)
When using repel, set xnudge and ynudge to 0 for optimal results
plot2

Then we scale the data. ScaleData will perform feature(gene)-level scaling, meaning that each feature will be centered to have a mean of 0 and scaled by the standard deviation of each feature (like how base R scale works).

Goal: To make the expression of all genes comparable by removing technical variation. How it works: For each gene, calculate the mean and standard deviation of its expression across all cells. For each cell, subtract the gene’s mean expression from its expression value. Divide the result by the standard deviation to get a scaled value. Result: Highly expressed genes no longer dominate the analysis, ensuring all genes have an equal chance of being influential.

  • Shifts the expression of each gene, so that the mean expression across cells is 0
  • Scales the expression of each gene, so that the variance across cells is 1
  • This step gives equal weight in downstream analyses, so that highly-expressed genes do not dominate
  • The results of this are stored in pbmc[[“RNA”]]$scale.data
  • By default, only variable features are scaled.
  • You can specify the features argument to scale additional features We’re also regressing out the effect of the total UMI counts and mitochondrial RNA. This is to avoid a large amount of the variance being due to differences in read counts between cells. You can add here any source of unwanted variation
srat_filt = ScaleData(srat_filt, vars.to.regress = c("nCount_RNA", "percent.mt"), 
                      verbose = T)
Regressing out nCount_RNA, percent.mt

  |                                                                                                     
  |                                                                                               |   0%
  |                                                                                                     
  |                                                                                               |   1%
  |                                                                                                     
  |=                                                                                              |   1%
  |                                                                                                     
  |=                                                                                              |   2%
  |                                                                                                     
  |==                                                                                             |   2%
  |                                                                                                     
  |==                                                                                             |   3%
  |                                                                                                     
  |===                                                                                            |   3%
  |                                                                                                     
  |===                                                                                            |   4%
  |                                                                                                     
  |====                                                                                           |   4%
  |                                                                                                     
  |====                                                                                           |   5%
  |                                                                                                     
  |=====                                                                                          |   5%
  |                                                                                                     
  |=====                                                                                          |   6%
  |                                                                                                     
  |======                                                                                         |   6%
  |                                                                                                     
  |======                                                                                         |   7%
  |                                                                                                     
  |=======                                                                                        |   7%
  |                                                                                                     
  |=======                                                                                        |   8%
  |                                                                                                     
  |========                                                                                       |   8%
  |                                                                                                     
  |========                                                                                       |   9%
  |                                                                                                     
  |=========                                                                                      |   9%
  |                                                                                                     
  |=========                                                                                      |  10%
  |                                                                                                     
  |==========                                                                                     |  10%
  |                                                                                                     
  |==========                                                                                     |  11%
  |                                                                                                     
  |===========                                                                                    |  11%
  |                                                                                                     
  |===========                                                                                    |  12%
  |                                                                                                     
  |============                                                                                   |  12%
  |                                                                                                     
  |============                                                                                   |  13%
  |                                                                                                     
  |=============                                                                                  |  13%
  |                                                                                                     
  |=============                                                                                  |  14%
  |                                                                                                     
  |==============                                                                                 |  14%
  |                                                                                                     
  |==============                                                                                 |  15%
  |                                                                                                     
  |===============                                                                                |  15%
  |                                                                                                     
  |===============                                                                                |  16%
  |                                                                                                     
  |================                                                                               |  16%
  |                                                                                                     
  |================                                                                               |  17%
  |                                                                                                     
  |=================                                                                              |  17%
  |                                                                                                     
  |=================                                                                              |  18%
  |                                                                                                     
  |==================                                                                             |  18%
  |                                                                                                     
  |==================                                                                             |  19%
  |                                                                                                     
  |===================                                                                            |  19%
  |                                                                                                     
  |===================                                                                            |  20%
  |                                                                                                     
  |===================                                                                            |  21%
  |                                                                                                     
  |====================                                                                           |  21%
  |                                                                                                     
  |====================                                                                           |  22%
  |                                                                                                     
  |=====================                                                                          |  22%
  |                                                                                                     
  |=====================                                                                          |  23%
  |                                                                                                     
  |======================                                                                         |  23%
  |                                                                                                     
  |======================                                                                         |  24%
  |                                                                                                     
  |=======================                                                                        |  24%
  |                                                                                                     
  |=======================                                                                        |  25%
  |                                                                                                     
  |========================                                                                       |  25%
  |                                                                                                     
  |========================                                                                       |  26%
  |                                                                                                     
  |=========================                                                                      |  26%
  |                                                                                                     
  |=========================                                                                      |  27%
  |                                                                                                     
  |==========================                                                                     |  27%
  |                                                                                                     
  |==========================                                                                     |  28%
  |                                                                                                     
  |===========================                                                                    |  28%
  |                                                                                                     
  |===========================                                                                    |  29%
  |                                                                                                     
  |============================                                                                   |  29%
  |                                                                                                     
  |============================                                                                   |  30%
  |                                                                                                     
  |=============================                                                                  |  30%
  |                                                                                                     
  |=============================                                                                  |  31%
  |                                                                                                     
  |==============================                                                                 |  31%
  |                                                                                                     
  |==============================                                                                 |  32%
  |                                                                                                     
  |===============================                                                                |  32%
  |                                                                                                     
  |===============================                                                                |  33%
  |                                                                                                     
  |================================                                                               |  33%
  |                                                                                                     
  |================================                                                               |  34%
  |                                                                                                     
  |=================================                                                              |  34%
  |                                                                                                     
  |=================================                                                              |  35%
  |                                                                                                     
  |==================================                                                             |  35%
  |                                                                                                     
  |==================================                                                             |  36%
  |                                                                                                     
  |===================================                                                            |  36%
  |                                                                                                     
  |===================================                                                            |  37%
  |                                                                                                     
  |====================================                                                           |  37%
  |                                                                                                     
  |====================================                                                           |  38%
  |                                                                                                     
  |=====================================                                                          |  38%
  |                                                                                                     
  |=====================================                                                          |  39%
  |                                                                                                     
  |======================================                                                         |  39%
  |                                                                                                     
  |======================================                                                         |  40%
  |                                                                                                     
  |======================================                                                         |  41%
  |                                                                                                     
  |=======================================                                                        |  41%
  |                                                                                                     
  |=======================================                                                        |  42%
  |                                                                                                     
  |========================================                                                       |  42%
  |                                                                                                     
  |========================================                                                       |  43%
  |                                                                                                     
  |=========================================                                                      |  43%
  |                                                                                                     
  |=========================================                                                      |  44%
  |                                                                                                     
  |==========================================                                                     |  44%
  |                                                                                                     
  |==========================================                                                     |  45%
  |                                                                                                     
  |===========================================                                                    |  45%
  |                                                                                                     
  |===========================================                                                    |  46%
  |                                                                                                     
  |============================================                                                   |  46%
  |                                                                                                     
  |============================================                                                   |  47%
  |                                                                                                     
  |=============================================                                                  |  47%
  |                                                                                                     
  |=============================================                                                  |  48%
  |                                                                                                     
  |==============================================                                                 |  48%
  |                                                                                                     
  |==============================================                                                 |  49%
  |                                                                                                     
  |===============================================                                                |  49%
  |                                                                                                     
  |===============================================                                                |  50%
  |                                                                                                     
  |================================================                                               |  50%
  |                                                                                                     
  |================================================                                               |  51%
  |                                                                                                     
  |=================================================                                              |  51%
  |                                                                                                     
  |=================================================                                              |  52%
  |                                                                                                     
  |==================================================                                             |  52%
  |                                                                                                     
  |==================================================                                             |  53%
  |                                                                                                     
  |===================================================                                            |  53%
  |                                                                                                     
  |===================================================                                            |  54%
  |                                                                                                     
  |====================================================                                           |  54%
  |                                                                                                     
  |====================================================                                           |  55%
  |                                                                                                     
  |=====================================================                                          |  55%
  |                                                                                                     
  |=====================================================                                          |  56%
  |                                                                                                     
  |======================================================                                         |  56%
  |                                                                                                     
  |======================================================                                         |  57%
  |                                                                                                     
  |=======================================================                                        |  57%
  |                                                                                                     
  |=======================================================                                        |  58%
  |                                                                                                     
  |========================================================                                       |  58%
  |                                                                                                     
  |========================================================                                       |  59%
  |                                                                                                     
  |=========================================================                                      |  59%
  |                                                                                                     
  |=========================================================                                      |  60%
  |                                                                                                     
  |=========================================================                                      |  61%
  |                                                                                                     
  |==========================================================                                     |  61%
  |                                                                                                     
  |==========================================================                                     |  62%
  |                                                                                                     
  |===========================================================                                    |  62%
  |                                                                                                     
  |===========================================================                                    |  63%
  |                                                                                                     
  |============================================================                                   |  63%
  |                                                                                                     
  |============================================================                                   |  64%
  |                                                                                                     
  |=============================================================                                  |  64%
  |                                                                                                     
  |=============================================================                                  |  65%
  |                                                                                                     
  |==============================================================                                 |  65%
  |                                                                                                     
  |==============================================================                                 |  66%
  |                                                                                                     
  |===============================================================                                |  66%
  |                                                                                                     
  |===============================================================                                |  67%
  |                                                                                                     
  |================================================================                               |  67%
  |                                                                                                     
  |================================================================                               |  68%
  |                                                                                                     
  |=================================================================                              |  68%
  |                                                                                                     
  |=================================================================                              |  69%
  |                                                                                                     
  |==================================================================                             |  69%
  |                                                                                                     
  |==================================================================                             |  70%
  |                                                                                                     
  |===================================================================                            |  70%
  |                                                                                                     
  |===================================================================                            |  71%
  |                                                                                                     
  |====================================================================                           |  71%
  |                                                                                                     
  |====================================================================                           |  72%
  |                                                                                                     
  |=====================================================================                          |  72%
  |                                                                                                     
  |=====================================================================                          |  73%
  |                                                                                                     
  |======================================================================                         |  73%
  |                                                                                                     
  |======================================================================                         |  74%
  |                                                                                                     
  |=======================================================================                        |  74%
  |                                                                                                     
  |=======================================================================                        |  75%
  |                                                                                                     
  |========================================================================                       |  75%
  |                                                                                                     
  |========================================================================                       |  76%
  |                                                                                                     
  |=========================================================================                      |  76%
  |                                                                                                     
  |=========================================================================                      |  77%
  |                                                                                                     
  |==========================================================================                     |  77%
  |                                                                                                     
  |==========================================================================                     |  78%
  |                                                                                                     
  |===========================================================================                    |  78%
  |                                                                                                     
  |===========================================================================                    |  79%
  |                                                                                                     
  |============================================================================                   |  79%
  |                                                                                                     
  |============================================================================                   |  80%
  |                                                                                                     
  |============================================================================                   |  81%
  |                                                                                                     
  |=============================================================================                  |  81%
  |                                                                                                     
  |=============================================================================                  |  82%
  |                                                                                                     
  |==============================================================================                 |  82%
  |                                                                                                     
  |==============================================================================                 |  83%
  |                                                                                                     
  |===============================================================================                |  83%
  |                                                                                                     
  |===============================================================================                |  84%
  |                                                                                                     
  |================================================================================               |  84%
  |                                                                                                     
  |================================================================================               |  85%
  |                                                                                                     
  |=================================================================================              |  85%
  |                                                                                                     
  |=================================================================================              |  86%
  |                                                                                                     
  |==================================================================================             |  86%
  |                                                                                                     
  |==================================================================================             |  87%
  |                                                                                                     
  |===================================================================================            |  87%
  |                                                                                                     
  |===================================================================================            |  88%
  |                                                                                                     
  |====================================================================================           |  88%
  |                                                                                                     
  |====================================================================================           |  89%
  |                                                                                                     
  |=====================================================================================          |  89%
  |                                                                                                     
  |=====================================================================================          |  90%
  |                                                                                                     
  |======================================================================================         |  90%
  |                                                                                                     
  |======================================================================================         |  91%
  |                                                                                                     
  |=======================================================================================        |  91%
  |                                                                                                     
  |=======================================================================================        |  92%
  |                                                                                                     
  |========================================================================================       |  92%
  |                                                                                                     
  |========================================================================================       |  93%
  |                                                                                                     
  |=========================================================================================      |  93%
  |                                                                                                     
  |=========================================================================================      |  94%
  |                                                                                                     
  |==========================================================================================     |  94%
  |                                                                                                     
  |==========================================================================================     |  95%
  |                                                                                                     
  |===========================================================================================    |  95%
  |                                                                                                     
  |===========================================================================================    |  96%
  |                                                                                                     
  |============================================================================================   |  96%
  |                                                                                                     
  |============================================================================================   |  97%
  |                                                                                                     
  |=============================================================================================  |  97%
  |                                                                                                     
  |=============================================================================================  |  98%
  |                                                                                                     
  |============================================================================================== |  98%
  |                                                                                                     
  |============================================================================================== |  99%
  |                                                                                                     
  |===============================================================================================|  99%
  |                                                                                                     
  |===============================================================================================| 100%
Centering and scaling data matrix

  |                                                                                                     
  |                                                                                               |   0%
  |                                                                                                     
  |===================                                                                            |  20%
  |                                                                                                     
  |======================================                                                         |  40%
  |                                                                                                     
  |=========================================================                                      |  60%
  |                                                                                                     
  |============================================================================                   |  80%
  |                                                                                                     
  |===============================================================================================| 100%

SCTransform

Another normalisation method is SCTransform. This method uses Pearson residuals for normalisation, and you can read more about it here: https://genomebiology.biomedcentral.com/articles/10.1186/s13059-019-1874-1. Running this will also automatically tell us what are the HVG. An updated tutorial for SCTransform V2 can be found here: https://satijalab.org/seurat/archive/v4.3/sctransform_v2_vignette.

Importantly, in Seurat, the result of each of these normalisations can be stored in a different assay - the more common method in the RNA assay, the scTransform method in SCT.

srat_filt = SCTransform(srat_filt, verbose = F,
                        vars.to.regress = c("nCount_RNA", "percent.mt"),  
                        variable.features.n = 5000,
                        seed.use = 123)
DefaultAssay(srat_filt) = "SCT"

We can compare the intersection of highly variable genes

list_hvg = list("RNA" = VariableFeatures(srat_filt, assay = "RNA"), 
                "SCT" = VariableFeatures(srat_filt, assay = "SCT"))
gplots::venn(list_hvg)

Dimensionality Reduction

scRNA-seq datasets commonly have ~10.000 data points (cells) and ~20.000 features (genes). This is of course very hard to visualise! DR algorithms help us visualize the major variability within a dataset.

PCA

PCA is the most common linear DR method used in statistics. It “compresses” the data into a set of uncorrelated dimensions (principal components), while also determining the weights for the features (genes) of each PC. We do linear dimension reduction (PCA) to determine the principal components of variation in the data. Note the genes associated with the first 5 PCs. Is there anything noticeable about them? If there are recognisable groups of genes associated with a particular PC this likely represents a strong biological signals (the most distinct cell type, variation in cell cycle stage, or a technical problem (low quality cells). Also of note, we will keep working with the SCT assay - note the “assay” argument.

srat_filt = RunPCA(srat_filt, verbose = T, assay = "SCT", npcs = 50)
PC_ 1 
Positive:  RPS27, LTB, IL7R, RPS18, RPS12, IL32, RPS29, EEF1A1, RPL13, TRAC 
       RPL10, RPS3, RPL3, RPS19, RPSA, RPL41, RPS6, RPL32, CD3D, RPL19 
       CD3E, TRBC2, RPS27A, RPL37, RPS2, RPS21, RPS5, RPS14, RPL23A, RPS15A 
Negative:  S100A9, S100A8, LYZ, CTSS, FTL, FCN1, VCAN, FOS, NEAT1, S100A12 
       CST3, MNDA, DUSP1, FTH1, TYROBP, PSAP, FCER1G, LGALS1, S100A6, AIF1 
       SRGN, S100A4, MS4A6A, FGL2, TYMP, KLF4, CD14, LST1, RGS2, SERPINA1 
PC_ 2 
Positive:  CD74, HLA-DRA, CD79A, HLA-DRB1, HLA-DPA1, IGHM, HLA-DPB1, MS4A1, CD79B, HLA-DQA1 
       IGHD, HLA-DQB1, IGKC, BANK1, LINC00926, TCL1A, CD37, CD22, TNFRSF13C, HLA-DQA2 
       VPREB3, FCER2, BCL11A, HLA-DRB5, SPIB, RALGPS2, IGLC2, HVCN1, PLPP5, MEF2C 
Negative:  IL32, NKG7, GZMA, CCL5, KLRB1, CTSW, IL7R, CST7, GZMM, TRAC 
       CD3E, HCST, PRF1, CD247, CD7, S100A4, CD3D, KLRG1, TRBC1, B2M 
       GZMK, GIMAP7, ARL4C, LCK, GNLY, KLRD1, HOPX, RORA, CD3G, MATK 
PC_ 3 
Positive:  IL7R, RPS12, LDHB, EEF1A1, RPL32, TPT1, RPL13, RPS27, RPS18, TCF7 
       RPL34, RPL30, RPS3A, TRAC, RPL39, RPS6, NOSIP, LTB, RPLP1, RPS14 
       RPL11, RPS28, TRABD2A, LEF1, CCR7, RPS15A, RPS27A, RPS29, S100A12, MAL 
Negative:  NKG7, GZMA, CST7, GNLY, CTSW, KLRD1, PRF1, CCL5, HOPX, GZMB 
       KLRF1, TRDC, FCGR3A, SPON2, FGFBP2, CLIC3, GZMM, MATK, CCL4, KLRB1 
       RHOC, IL2RB, ADGRG1, CD74, APMAP, APOBEC3G, GZMH, HLA-DPA1, CMC1, EFHD2 
PC_ 4 
Positive:  S100A12, VCAN, S100A8, PLBD1, MNDA, AC020656.1, FOS, CD36, CD14, CEBPD 
       CES1, MS4A6A, APLP2, S100A9, ALOX5AP, GRN, CYP1B1, ITGAM, NCF1, ANXA1 
       CSF3R, MEGF9, CSTA, IGKC, PADI4, MCL1, GAPDH, QPCT, MGST1, METTL9 
Negative:  FCGR3A, CDKN1C, SMIM25, MS4A7, TCF7L2, HES4, SAT1, RHOC, SIGLEC10, LST1 
       HLA-DPA1, COTL1, SERPINA1, AIF1, IFITM3, FTH1, CSF1R, HMOX1, MTSS1, SPI1 
       NAAA, MAFB, ZNF703, WARS, MARCKS, IFI30, LYN, LRRC25, ABI3, CAMK1 
PC_ 5 
Positive:  CD79A, MS4A1, IGHD, CD79B, LINC00926, NKG7, GZMA, CD37, CD22, CST7 
       BANK1, FCER2, TCL1A, TNFRSF13C, CD52, HLA-DQB1, KLRB1, HOPX, FCRL1, PRF1 
       FOS, DUSP1, CTSW, VPREB3, KLRG1, IGLC2, LTB, CEBPD, GNLY, GZMM 
Negative:  CST3, PTCRA, SERPINF1, SMIM5, IL3RA, PPP1R14B, ITM2C, CLEC4C, APP, SMPD3 
       LILRA4, PLD4, DNASE1L3, DERL3, SCT, MAPKAPK2, AL096865.1, IRF7, GSN, SEC61B 
       TPM2, PACSIN1, UGCG, LRRC26, HSP90B1, RPS6KA4, NUCB2, GAS6, C12orf75, CCDC88A 

The result of the PCA is stored in the reductions slot, here as $pca:

srat_filt@reductions
$pca
A dimensional reduction object with key PC_ 
 Number of dimensions: 50 
 Number of cells: 1030 
 Projected dimensional reduction calculated:  FALSE 
 Jackstraw run: FALSE 
 Computed using assay: SCT 

We can plot the first two principal components using the DimPlot() function.

DimPlot(srat_filt, reduction = "pca", dims = 1:2)

Looking at the loadings (i.e. gene contributions) of each PC can already tell us a few things about the biological variability in the sample

VizDimLoadings(srat_filt, dims = 1:3, reduction = "pca", ncol = 3)

Here similar, but more PCs and plotting expression of the top 500 cells in each PC, as well as the top genes

DimHeatmap(srat_filt, reduction = "pca", assays = "SCT", dims = 1:12, 
           cells = 500, balanced = TRUE, ncol = 3)

We need to pick a number of PCs which capture most of the variance in the data. These will be used for downstream analysis. How many PCs should we use to describe the data? This plot shows how much variance is captured by each PC. This is mostly an “eyeballing” method, to select approximately when the variance explained per PC flattens, which would indicate that most of the datasets variability is present in those top PCs. It is also relevant to note that, should any PC reflect unwanted variability (e.g. sequencing depth per cell, or other technical factors) it can be excluded.

ElbowPlot(srat_filt, ndims = 50)

A large proportion of the variation is captured by 15 components and there is a drop off thereafter. Let’s go with 20.

ncomp = 20

Non-Linear Dimensionality Reduction

Non-linear dimension reduction methods such as UMAP and TSNE take the PCA data as a starting point, but are able to take more complex (non-linear) patterns hidden in the data and represent them in only two dimensions (which humans are good at examining).These methods will take the main PCs determined above and project them in a lower dimension space, at the cost of distorting distances between points to form organised clumps. Thus, the output of these methods should only be used for visualisation, as a general guide for what the data “looks” like.

srat_filt <- RunUMAP(srat_filt, dims = 1:ncomp, verbose = F)

Plot the projection

DimPlot(srat_filt, reduction = "umap")

Bear in mind - do not overinterpret these plots! tSNE and UMAP serve as simple ways to represent your data’s variability in 2D, and allow for eyeballing the approximate number of cell populations in your data. However, one cannot tell how different cell populations are from each other based on their distances, or even how many “real” populations exist - this comes down to interpretation.

If we plot the expression of the B cell marker CD79A (below), we can see that there is a clear cluster of B cells

FeaturePlot(srat_filt, reduction = "umap", features = c('CD79A'))

Clustering

We can use the chosen PCs to get a neighborhood graph for all cells. This graph is done in PC space, determining which cells are more similar to which by their distances in the chosen top PCs.

red = "pca"
srat_filt = FindNeighbors(srat_filt, dims = 1:ncomp, verbose = T,
                          reduction = red, graph.name = paste0(red, ncomp))
Computing nearest neighbor graph
Computing SNN
Only one graph name supplied, storing nearest-neighbor graph only

This allows us to detect clusters of cells to then be interpreted. For this we can use community detection algorithms - methods to discover associated groups of points in graphs. We’re using algorithm 2 (Louvain algorithm with multilevel refinement), and we’re getting clusters for multiple resolutions. A higher resolution can return more clusters. Leiden clustering (another algorithm) might currently be the best choice, but it’s more difficult to install.

srat_filt = FindClusters(srat_filt, algorithm = 2, verbose = T, 
                         graph.name = paste0(red, ncomp),
                         resolution = seq(0.2, 1, 0.1))
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 1030
Number of edges: 9627

Running Louvain algorithm with multilevel refinement...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9282
Number of communities: 5
Elapsed time: 0 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 1030
Number of edges: 9627

Running Louvain algorithm with multilevel refinement...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9034
Number of communities: 6
Elapsed time: 0 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 1030
Number of edges: 9627

Running Louvain algorithm with multilevel refinement...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8789
Number of communities: 6
Elapsed time: 0 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 1030
Number of edges: 9627

Running Louvain algorithm with multilevel refinement...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8624
Number of communities: 8
Elapsed time: 0 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 1030
Number of edges: 9627

Running Louvain algorithm with multilevel refinement...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8483
Number of communities: 9
Elapsed time: 0 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 1030
Number of edges: 9627

Running Louvain algorithm with multilevel refinement...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8350
Number of communities: 9
Elapsed time: 0 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 1030
Number of edges: 9627

Running Louvain algorithm with multilevel refinement...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8223
Number of communities: 10
Elapsed time: 0 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 1030
Number of edges: 9627

Running Louvain algorithm with multilevel refinement...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8101
Number of communities: 10
Elapsed time: 0 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 1030
Number of edges: 9627

Running Louvain algorithm with multilevel refinement...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7978
Number of communities: 10
Elapsed time: 0 seconds
head(srat_filt@meta.data)
NA

And so we can have a look at all clustering resolutions

plt_list = list()
# iterate each clustering resolution
for(cl in colnames(srat_filt@meta.data)[grepl(paste0("pca", ncomp, "_"), 
                                              colnames(srat_filt@meta.data))]){
srat_filt = SetIdent(srat_filt, value = cl) # set the resolution as default identity
plt_list[[cl]] = DimPlot(srat_filt, reduction = "umap", 
                         label = T, raster = F, pt.size = 0.2)+
    labs(subtitle = cl)+
    theme(legend.position = "none",
          aspect.ratio = 1)
}
cowplot::plot_grid(plotlist = plt_list, ncol = 3)

NA
NA

UMAP/tSNE can be a good visual guide for how many clusters can be expected. Alternatively, one can use a package such as clustree to choose a clustering resolution. Resolution can be chosen as the one before clusters start becoming unstable.

clustree::clustree(srat_filt, prefix = paste0("pca", ncomp, "_res."), node_colour = "sc3_stability")

Then we define that resolution as the default identity

cl_use = paste0(red, ncomp, "_res.", 0.8)
srat_filt = SetIdent(srat_filt, value = cl_use)

Cell type identification

We can now start to ask “which of these clusters are real cell populations?”. And by real, we mean “biologically relevant”, i.e. they represent a cell type/state in our system being studied.

Since the UMAP plot is generated based on a PCA that should reflect some recognized biological variability, we can expect that the “clumps” that it forms are to some degree related to this. So one strategy is to pick a clustering resolution that coincides with most of these perceived clumps.

Another sensible approach is to use marker genes for expected cell populations in our data. We can check for their expression to have an approximate feeling of what each cluster or group of clusters is before we get their marker genes

We have our clusters, now let’s look at where some markers of interest are expressed. Based on published data, we know that:

# Plot the expression values of each markers on the UMAP
FeaturePlot(srat_filt, features = c("CD79A", "CST3", "CD3D"), pt.size = 0.1, label = TRUE, reduction = 'umap')

As violin plot

VlnPlot(srat_filt, features  = c("CD79A", "CST3", "CD3D"))

What about 7? What are the different custers within each cell type. Are we sure they are correctly assigned?

We can calculate the marker genes for a chosen clustering resolution. This is done as a 1 vs rest comparison.

Because of the nature of large sample size in scRNA-seq data (one cell is one sample), it is strongly recommended to not only look at p-values, but also detection rate of the gene in the cluster (pct) and fold change (logFC) between cells in and outside the cluster.

srat_filt = SetIdent(srat_filt, value = cl_use)

mk_clusters = FindAllMarkers(srat_filt, assay = "SCT", test.use = "wilcox",
                             logfc.threshold = 0.2, verbose = T, only.pos = T)
Calculating cluster 0
Calculating cluster 1
Calculating cluster 2
Calculating cluster 3
Calculating cluster 4
Calculating cluster 5
Calculating cluster 6
Calculating cluster 7
Calculating cluster 8
Calculating cluster 9
mk_top = mk_clusters %>% 
  group_by(cluster)  %>%  # for each cluster
  filter(p_val_adj<=0.05)  %>%  # only p-value below 0.05
  top_n(n = 20, wt = avg_log2FC) # top genes per cluster, ranked by logFC

mk_top

We can also directly compare pairs of cell populations (1 vs 1 comparison).

mk_comp = FindMarkers(srat_filt, ident.1 = "2", ident.2 = "5", assay = "SCT", 
                   test.use = "wilcox", logfc.threshold = 0.2)

Assigning cell type identity to clusters

Fortunately in the case of this dataset, we can use canonical markers to easily match the unbiased clustering to known cell types. Azimuth is a good reference for marker genes, with different levels of “granularity” https://azimuth.hubmapconsortium.org/references/#Human%20-%20PBMC

Investigate the markers, are they widespread within the cluster? Or only expressed in few cells?

Cell type identification/annotation is one of the hardest and most laborious tasks in scRNAseq analysis. You will need to play with clustering resolutions, sub-clustering, and look for markers in lots of papers and databases to annotate your cells (always depending on the level of detail you wish to achieve). Moreover, available methods exist to automatically classify cells into cell types based on previously annotated data. Some of those resources are listed here: https://www.10xgenomics.com/analysis-guides/web-resources-for-cell-type-annotation

It often happens that analysing the total sample does not provide a coherent picture of all subpopulations present. This can happen because the difference between certain cell subtypes might not be large enough to detect them, compared to more distinct populations.

Seurat includes the function below to easily obtain subclusters from specific clusters. However, our advice is for you to not simply subset and cluster, but rather recalculate HVG, PCA, and NN graph for the cell subset. This is because we might find that the initial set of HVG/PCs does not reflect their variability.

srat_filt = FindSubCluster(srat_filt, cluster = "0", graph.name = paste0(red, ncomp))
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 175
Number of edges: 1423

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.6309
Number of communities: 3
Elapsed time: 0 seconds
table(srat_filt@meta.data$sub.cluster)

0_0 0_1 0_2   1   2   3   4   5   6   7   8   9 
 82  58  35 158 134 130 125 117  60  52  43  36 

Seurat also includes a way to score groups of cells based on their cell cycle state. This can be helpful if we’re for example looking for proliferating or stem cell populations.

This function is a specific case of the AddModuleScore function, which quantifies the presence of any gene module of choice.

srat_filt = CellCycleScoring(srat_filt, 
                             s.features = cc.genes$s.genes, 
                             g2m.features = cc.genes$g2m.genes, 
                             set.ident = F,
                             nbin = 15)
Warning: The following features are not present in the object: TYMS, DTL, MLF1IP, CCNE2, RAD51, RRM2, CDC45, CDC6, EXO1, DSCC1, E2F8, not searching for symbol synonymsWarning: The following features are not present in the object: CDK1, UBE2C, BIRC5, TOP2A, FAM64A, CCNB2, CKAP2L, BUB1, KIF11, GTSE1, HJURP, CDCA3, HN1, CDC20, TTK, CDC25C, KIF2C, DLGAP5, CDCA2, CDCA8, HMMR, ANLN, NEK2, CENPA, not searching for symbol synonyms
DimPlot(srat_filt, reduction = "umap", group.by = "Phase")

Simpler (and often more accurate) ways of doing this often consist on plotting expression of cell cycle associated genes

FeaturePlot(srat_filt, features = c("TOP2A", "MKI67", "CDK1"), order = T)
Warning: Could not find TOP2A in the default search locations, found in ‘RNA’ assay insteadWarning: Could not find CDK1 in the default search locations, found in ‘RNA’ assay instead

#saveRDS(srat_filt, file = "Example_filt_srat.RDS")
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIHNpbmdsZS1jZWxsIFJOQS1zZXEgYW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KClRoZSBhaW0gb2YgdGhpcyBub3RlYm9vayBpcyB0byBkZW1vbnN0cmF0ZSBob3cgdG8gdXNlIHRoZSBTZXVyYXQgUiBwYWNrYWdlIHRvIHByb2Nlc3Mgc2NSTkEtc2VxIGRhdGEuCgpNb3JlIG9uIGJhc2ljIGFuYWx5c2lzIHdpdGggU2V1cmF0IGluIDxodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FydGljbGVzL3BibWMza190dXRvcmlhbC5odG1sPi4KClNlZSBhbHNvIDxodHRwczovL2dpdGh1Yi5jb20vcXVhZGJpb2xhYi9zY1JOQXNlcV9hbmFseXNpc192aWduZXR0ZS9ibG9iL21hc3Rlci9UdXRvcmlhbC5tZD4gZm9yIGEgbW9yZSBpbi1kZXB0aCB0dXRvcmlhbC4KCgojIEdlbmVyYWwgc2V0dXAKTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCgpOb3RlLCB0dXRvcmlhbCB3aXRoIHNldXJhdCBWNSwgd2hpY2ggaXMgc3Vic3RhbnRpYWxseSBkaWZmZXJlbnQgZnJvbSBlYXJsaWVyIHZlcnNpb24uCjxodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FydGljbGVzL2Fubm91bmNlbWVudHMuaHRtbD4KClRoZSBkb3dubG9hZCBvZiB2ZXJzaW9uIDUgb3IgYWJvdmUgc2hvdWxkIGhhcHBlbiBhdXRvbWF0aWNhbGx5LiBJZiB5b3UgaGF2ZSB3b3JrZWQgd2l0aCBwcmV2aW91cyB2ZXJzaW9ucywgZG93bmxvYWQgdjUgb3IgYWJvdmUgZm9sbG93aW5nIDxodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FydGljbGVzL2luc3RhbGwuaHRtbD4KCmBgYHtyfQojIEJpb2NvbmR1Y3RvciBwYWNrYWdlIGluc3RhbGxlcgppZighcmVxdWlyZSgiQmlvY01hbmFnZXIiLCBxdWlldGx5ID0gVFJVRSkpCiAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQoKcGFja2FnZXNfdXNlID0gYygiU2V1cmF0IiwgIyBwYWNrYWdlIHdpdGggY29udmVuaWVudGx5IG9yZ2FuaXNlZCBzY1JOQS1zZXEgbWV0aG9kcwogICAgICAgICAgICAgICAgICJjbHVzdHJlZSIsICMgYSBtZXRob2QgdG8gaGVscCBjaG9vc2UgY2x1c3RlcmluZyByZXNvbHV0aW9uCiAgICAgICAgICAgICAgICAgImRwbHlyIiwgICMgaW1wb3J0YW50IHRvIHdvcmsgd2l0aCBzb21lIGRhdGEgc3RydWN0dXJlcyBpbiBSCiAgICAgICAgICAgICAgICAgImdncGxvdDIiLCAjIHBsb3R0aW5nCiAgICAgICAgICAgICAgICAgInBhdGNod29yayIsICMgYXNzZW1ibGluZyBwbG90cwogICAgICAgICAgICAgICAgICJjb3dwbG90IiwgIyBhc3NlbWJsaW5nIHBsb3RzCiAgICAgICAgICAgICAgICAgImdwbG90cyIpICMgYWxzbyBwbG90dGluZykKCgojIGZvciBlYWNoIHBhY2thZ2UgbGlzdGVkOiBpZiBub3QgaW5zdGFsbGVkLCBpbnN0YWxsLCBvdGhlcndpc2UgbG9hZCBpdApmb3IocCBpbiBwYWNrYWdlc191c2UpewogIGlmKCEocCAlaW4lIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKSkpewogICAgQmlvY01hbmFnZXI6Omluc3RhbGwocCkKICAgIGxpYnJhcnkocCwgY2hhcmFjdGVyLm9ubHkgPSBUKQogIH0gZWxzZXsKICAgIGxpYnJhcnkocCwgY2hhcmFjdGVyLm9ubHkgPSBUKQogIH0KfQoKIyBzZXQgc2VlZCBmb3IgcmFuZG9taXNhdGlvbiAoZS5nLiBVTUFQIGRpbWVuc2lvbiByZWR1Y3Rpb24pCnNldC5zZWVkKDEyMykKYGBgCgoKCiMgTG9hZCBkYXRhCkxvYWQgeW91ciBkYXRhc2V0LgpGb3IgdGhpcyB0dXRvcmlhbCwgd2Ugd2lsbCB1c2UgYSBkYXRhc2V0IG9mIDFrIFBlcmlwaGVyYWwgYmxvb2QgbW9ub251Y2xlYXIgY2VsbHMgKFBCTUNzKSBmcm9tIGEgaGVhbHRoeSBkb25vciBmcm9tIDEweCBHZW5vbWljcy4gCjxodHRwczovL3d3dy4xMHhnZW5vbWljcy5jb20vZGF0YXNldHMvMS1rLXBibS1jcy1mcm9tLWEtaGVhbHRoeS1kb25vci12LTMtY2hlbWlzdHJ5LTMtc3RhbmRhcmQtMy0wLTA+CgpUbyByZWFkIHRoZSBkYXRhLCB3ZSB3aWxsIHVzZSBhIGRlZGljYXRlZCBmdW5jdGlvbiB0aGF0IHJlYWRzIGNlbGxyYW5nZXIgb3V0cHV0cy4gSW4gdGhpcyBjYXNlLCB3ZSB3aWxsIHJlYWQgdGhlIGZpbHRlcmVkIG1hdHJpeC4KClRoZSAqKlJlYWQxMFgoKSoqIGZ1bmN0aW9uIHJlYWRzIGluIHRoZSBvdXRwdXQgb2YgdGhlIGNlbGxyYW5nZXIgcGlwZWxpbmUgZnJvbSAxMFgsIHJldHVybmluZyBhIHVuaXF1ZSBtb2xlY3VsYXIgaWRlbnRpZmllZCAoVU1JKSBjb3VudCBtYXRyaXguIFRoZSB2YWx1ZXMgaW4gdGhpcyBtYXRyaXggcmVwcmVzZW50IHRoZSBudW1iZXIgb2YgbW9sZWN1bGVzIGZvciBlYWNoIGZlYXR1cmUgKGkuZS4gZ2VuZTsgcm93KSB0aGF0IGFyZSBkZXRlY3RlZCBpbiBlYWNoIGNlbGwgKGNvbHVtbikuIE5vdGUgdGhhdCBtb3JlIHJlY2VudCB2ZXJzaW9ucyBvZiBjZWxscmFuZ2VyIG5vdyBhbHNvIG91dHB1dCB1c2luZyB0aGUgaDUgZmlsZSBmb3JtYXQsIHdoaWNoIGNhbiBiZSByZWFkIGluIHVzaW5nIHRoZSBSZWFkMTBYX2g1KCkgZnVuY3Rpb24gaW4gU2V1cmF0LgoKYGBge3J9ClBCTUNfZGF0YSA9IFJlYWQxMFgoZGF0YS5kaXIgPSAiL1VzZXJzL21pZ3VlbC9Eb2N1bWVudHMvc2NSTkFzZXEgY291cnNlLzIwMjUvZmlsdGVyZWRfZmVhdHVyZV9iY19tYXRyaXhfMWsvIikKCiNQQk1DX2RhdGEgPSBSZWFkMTBYKGRhdGEuZGlyID0gIkFERF9EQVRBU0VUX1BBVEgiKQoKYGBgCgpUaGlzIGltcG9ydHMgdGhlIG1hdHJpeCBvZiBjb3VudHMgaW50byBhIG9iamVjdHMgb2YgY2xhc3MgZGdDTWF0cml4LgpMZXRzIGV4YW1pbmUgYSBmZXcgZ2VuZXMgaW4gdGhlIGZpcnN0IHRoaXJ0eSBjZWxscwpgYGB7cn0KUEJNQ19kYXRhW2MoIkNEM0QiLCAiVENMMUEiLCAiTVM0QTEiKSwgMTozMF0KYGBgClRoZSAuIHZhbHVlcyBpbiB0aGUgbWF0cml4IHJlcHJlc2VudCAwcyAobm8gbW9sZWN1bGVzIGRldGVjdGVkKS4gU2luY2UgbW9zdCB2YWx1ZXMgaW4gYW4gc2NSTkEtc2VxIG1hdHJpeCBhcmUgMCwgU2V1cmF0IHVzZXMgYSBzcGFyc2UtbWF0cml4IHJlcHJlc2VudGF0aW9uIHdoZW5ldmVyIHBvc3NpYmxlLiBUaGlzIHJlc3VsdHMgaW4gc2lnbmlmaWNhbnQgbWVtb3J5IGFuZCBzcGVlZCBzYXZpbmdzIGZvciBEcm9wLXNlcS9pbkRyb3AvMTB4IGRhdGEuCgoKV2UgY2FuIHVzZSB0aGVzZSB0byBtYWtlIGEgU2V1cmF0IG9iamVjdCwgd2hpY2ggaXMgd2hhdCB3ZSB3aWxsIHVzZSBpbiB0aGUgYW5hbHlzaXM6CmBgYHtyfQpzcmF0IDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBQQk1DX2RhdGEsIHByb2plY3QgPSAiUEJNQ18xMHgiKQpgYGAKCiMgQmFzaWMgU2V1cmF0IG9iamVjdCBzdHJ1Y3R1cmUKClRoZXJlIGFyZSB0d28gaW1wb3J0YW50IGNvbXBvbmVudHMgb2YgdGhlIFNldXJhdCBvYmplY3QgdG8gYmUgYXdhcmUgb2Y6CgpUaGUgKipAbWV0YS5kYXRhKiogc2xvdCwgd2hpY2ggc3RvcmVzIG1ldGFkYXRhIGZvciBvdXIgZHJvcGxldHMvY2VsbHMgKGUuZy4gd2hpY2ggYmF0Y2ggb2Ygc2FtcGxlcyB0aGV5IGJlbG9uZyB0bywgdG90YWwgY291bnRzLCB0b3RhbCBudW1iZXIgb2YgZGV0ZWN0ZWQgZ2VuZXMsIGV0Yy4pLgoKVGhlICoqQGFzc2F5cyBzbG90KiosIHdoaWNoIHN0b3JlcyB0aGUgbWF0cml4IG9mIHJhdyBjb3VudHMsIGFzIHdlbGwgYXMgKGZ1cnRoZXIgZG93bikgbWF0cmljZXMgb2Ygbm9ybWFsaXNlZC90cmFuc2Zvcm1lZCBkYXRhLgoKU3RhcnRpbmcgd2l0aCBvdXIgbWV0YWRhdGEgc2xvdDoKYGBge3J9CmhlYWQoc3JhdEBtZXRhLmRhdGEpCmBgYApUaGUgcm93bmFtZXMgb2YgdGhpcyB0YWJsZSByZXByZXNlbnQgdGhlIGRyb3BsZXQvY2VsbCBpZHMsIGdpdmVuIGluIHRoZSByb3duYW1lcyBvZiB0aGlzIHRhYmxlLiBUaGUg4oCcb3JpZy5pZGVudOKAnSBoYXMgdGhlIHNhbXBsZSBuYW1lcyB3ZSAgc3BlY2lmaWVkIGVhcmxpZXIuIEFuZCB3ZSBjYW4gc2VlIHRoYXQgU2V1cmF0IGF1dG9tYXRpY2FsbHkgY2FsY3VsYXRlZCB0b3RhbCBVTUkgY291bnRzIChuQ291bnRfUk5BKSBhbmQgdGhlIHRvdGFsIG51bWJlciBvZiBkZXRlY3RlZCBnZW5lcyAobkZlYXR1cmVfUk5BKSBpbiBlYWNoIGRyb3BsZXQuClRoZSBtZXRhLmRhdGEgY29udGFpbnMgaW5mb3JtYXRpb24gZm9yIGVhY2ggY2VsbCBvbiBrZXkgbWVhc3VyZW1lbnRzIChudW1iZXIgb2YgVU1JLCBudW1iZXIgb2YgZ2VuZXMsIC4uKSwgZXhwZXJpbWVudGFsIHZhcmlhYmxlcyAoYmF0Y2gsIGNvbmRpdGlvbiwgLi4uKSwgb3Igb3V0cHV0IGxhYmVscyAoY2x1c3RlcnMpCgpXZSBjYW4gYWxzbyBsb29rIGF0IGNlbGwgYW5kIGdlbmUgbmFtZXMgYnkgYWNjZXNzaW5nIHRoZSBjb2xuYW1lcyBhbmQgcm93bmFtZXMsIHJlc3BlY3RpdmVsbHkuIApgYGB7cn0KaGVhZChjb2xuYW1lcyhzcmF0KSkKYGBgCgoKYGBge3J9CmhlYWQocm93bmFtZXMoc3JhdCkpCmBgYApOb3RlOiB0aGUgY2VsbCBuYW1lcyBhcmUgdGhlICoqUk9XTkFNRVMqKiBvZiB0aGUgKipAbWV0YS5kYXRhKiogc2xvdCwgYnV0IHRoZSAqKkNPTE5BTUVTKiogb2YgdGhlIFNldXJhdCBvYmplY3QKCk1vdmluZyBvbiB0byB0aGUgYXNzYXlzIHNsb3QsIHdlIGNhbiBzZWUgdGhhdCBpdCBjdXJyZW50bHkgY29udGFpbnMganVzdCB0aGUgY291bnQgZGF0YSwgY2FsbGVkICRSTkEuIE5vcm1hbGlzZWQgZGF0YSBvZiBkaWZmZXJlbnQgc29ydHMgd2lsbCBhbHNvIGdldCBzdG9yZWQgaGVyZS4KYGBge3J9CnNyYXRAYXNzYXlzCmBgYAoKQmVpbmcgYXdhcmUgb2YgdGhlIGFjdGl2ZSBhc3NheSBpcyBpbXBvcnRhbnQgd2hlbiBkb2luZyBkaWZmZXJlbnQgdHlwZXMgb2YgYW5hbHlzaXMgYmVjYXVzZSB0b29scyB3aWxsIHRyeSB0byB1c2UgdGhlIGFjdGl2ZSBhc3NheSBieSBkZWZhdWx0IGlmIHRoZXkgY2FuLiBUaGlzIHdpbGwgbWFrZSBtb3JlIHNlbnNlIGFzIHdlIHByb2NlZWQgd2l0aCB0aGUgZG93bnN0cmVhbSBhbmFseXNpcy4gWW91IHdpbGwgYWxzbyBoYXZlIG1vcmUgdGhhbiBvbmUgYXNzYXkgaW4gdGhlIGNhc2Ugb2YgbXVsdGltb2RhbCBkYXRhOiB5b3UgY291bGQgaGF2ZSBhbiBhc3NheSBmb3IgUk5BIGFuZCBhbm90aGVyIGZvciBBVEFDIG9yIHByb3RlaW4gZGF0YSAoQ0lURS1TZXEpLiBZb3UgY2FuIGNoZWNrIHRoZSBhY3RpdmUgYXNzYXkgYW5kIG1vZGlmeSBpdCBieSBydW5uaW5nIHRoZSBmb2xsb3dpbmcgY29tbWFuZHM6CmBgYHtyfQpzcmF0QGFjdGl2ZS5hc3NheQoKRGVmYXVsdEFzc2F5KHNyYXQpIDwtICdSTkEnCmBgYAoKCgoKIyBRdWFsaXR5IGNvbnRyb2wKVGhlIG1haW4gcXVhbGl0eSBjb250cm9sIG1ldHJpY3MgZm9yIHNjUk5BLXNlcSBkYXRhIGFyZToKLSAgIG5Db3VudF9STkE6IGNlbGxzIHdpdGggdmVyeSBsb3cgY291bnRzIGFyZSBsaWtlbHkgbm90IHZlcnkgaW5mb3JtYXRpdmUsIG9yIG1heSBiZSBkZWFkIGNlbGxzLiBIb3dldmVyLCBzb21lIGNlbGwgdHlwZXMgKGUuZy4gcGxhdGVsZXRzLCBzcGVybSkgYXJlIHNtYWxsZXIgdGhhbiB1c3VhbCwgYW5kIHRodXMgYXJlIGV4cGVjdGVkIHRvIGhhdmUgdmVyeSBmZXcgY291bnRzIChcPDUwMCkuIEtub3dpbmcgeW91ciBiaW9sb2dpY2FsIHN5c3RlbSBpcyBpbXBvcnRhbnQhCgotICAgbkZlYXR1cmVfUk5BOiB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBnZW5lcyBwZXIgY2VsbCBjYW4gaW5kaWNhdGUgYWdhaW4gd2hldGhlciBhIGNlbGwgaXMgaW5mb3JtYXRpdmUgb3Igbm90IChmZXcgZ2VuZXMpLCBidXQgYWxzbyBpZiB0aGV5IG1pZ2h0IGJlIGEgZG91YmxldC4gRG91YmxldHMgKGFuZCBpbiBwYXJ0aWN1bGFyIHRob3NlIG9yaWdpbmF0aW5nIGZyb20gMiBkaWZmZXJlbnQgY2VsbCB0eXBlcykgd2lsbCBoYXZlIGEgbGFyZ2VyIGRpdmVyc2l0eSBvZiBnZW5lcyBkZXRlY3RlZC4gT3RoZXIgbWV0aG9kcyBleGlzdCB0byBzcGVjaWZpY2FsbHkgZGV0ZWN0IGRvdWJsZXRzIGp1c3QgZnJvbSBnZW5lIGV4cHJlc3Npb24gKGUuZy4gc2NydWJsZXQsIERvdWJsZXRGaW5kZXIsIC4uLikgYnV0IHRoZXkgd2lsbCBub3QgYmUgY292ZXJlZCBoZXJlLgoKLSAgIHBlcmNlbnQubXQ6IGNlbGxzIG5lZ2F0aXZlbHkgYWZmZWN0ZWQgYnkgdGlzc3VlIHByb2Nlc3NpbmcgYW5kIGlzb2xhdGlvbiB0ZW5kIHRvIGhhdmUgdGhpcyBtZXRyaWMgaGlnaGVyLiBUaGlzIGlzIGxpa2VseSBiZWNhdXNlLCBmb3IgZHlpbmcgY2VsbHMsIHRoZSBtZW1icmFuZSB3aWxsIGJ1cnN0IGFuZCByZWxlYXNlIGN5dG9wbGFzbWljIFJOQSwgYnV0IGxlYXZlIHRoZSBtaXRvY2hvbmRyaWEgaW50YWN0LiBIb3dldmVyLCBzb21lIGNlbGwgdHlwZXMgbWF5IGhhdmUgbXVjaCBoaWdoZXIgbWl0b2Nob25kcmlhbCByZWFkcyAoZS5nLiBoZXBhdG9jeXRlcyksIHNvIGFnYWluLCBrbm93aW5nIHlvdXIgc3lzdGVtIGlzIGVzc2VudGlhbCEKCkdldCAlIG9mIHJlYWRzIGNvbWluZyBmcm9tIE1UIHRyYW5zY3JpcHRzLiBJbiBtb3N0IHNwZWNpZXMsIHRoZXNlIGNhbiBiZSBmb3VuZCBlaXRoZXIgYnkgdGhlaXIgZ2VuZSBuYW1lIChzdGFydGluZyB3aXRoICJtdC0iKSBvciBieSBmaW5kaW5nIGlmIHRoZSBnZW5lIGlzIGVuY29kZWQgaW4gdGhlIG1pdG9jaG9uZHJpYWwgY2hyb21vc29tZSAocmV0cmlldmluZyB0aGF0IGluZm9ybWF0aW9uIGZyb20sIGZvciBleGFtcGxlLCB0aGUgRW5zZW1ibCBCaW9NYXJ0KS4KSW4gdGhlIGh1bWFuIGdlbm9tZSwgYWxsIG1pdG9jaG9uZHJpYWwgZ2VuZXMgc3RhcnQgd2l0aCBNVC4gVGhpcyBpcyBwcm9iYWJseSBub3QgdGhlIGNhc2UgZm9yIHlvdXIgbm9uLWh1bWFuIHNwZWNpZXMgb2YgaW50ZXJlc3QsIGFsdGhvdWdoIGZvciBtb3VzZSBpdCBpcyB1c3VhbGx5IOKAmE104oCZLgpgYGB7cn0Kc3JhdCA9IFBlcmNlbnRhZ2VGZWF0dXJlU2V0KHNyYXQsIGNvbC5uYW1lID0gInBlcmNlbnQubXQiLCBhc3NheSA9ICJSTkEiLCBwYXR0ZXJuID0gIl5NVC0iKSAKCmhlYWQoc3JhdEBtZXRhLmRhdGEpCmBgYAoKCkFub3RoZXIgc2ltcGxlIG1ldHJpYyB0aGF0IHdlIGNhbiBxdWlja2x5IGNhbGN1bGF0ZSBpcyB0aGUgbnVtYmVyIG9mIGdlbmVzIGRldGVjdGVkIGluIHRoZSB3aG9sZSBkYXRhc2V0IChpLmUuIHRob3NlIHRoYXQgYXJlIHRydWUgZm9yIHRoaXMgc3RhdGVtZW50KToKYGBge3J9CnRhYmxlKHJvd1N1bXMoc3JhdEBhc3NheXMkUk5BKSA+IDApCmBgYAoKCldlIGNhbiB1c2UgdGhlIFZsblBsb3QoKSBmdW5jdGlvbiB0byB2aXN1YWxpc2UgZGF0YSBzdG9yZWQgaW4gb3VyIG1ldGFkYXRhIHNsb3QuIFRoaXMgZnVuY3Rpb24gd2lsbCBnZW5lcmF0ZSB2aW9saW4gcGxvdHMgd2l0aCBzYW1wbGVzIG9uIHRoZSB4LWF4aXMgYW5kIHRoZSB2YXJpYWJsZSBvZiBpbnRlcmVzdCBvbiB0aGUgeS1heGlzLiBXZSB3aWxsIHVzZSB0aGVzZSBtZXRyaWNzIHRvIHNlbGVjdCB3aGljaCBjZWxscyAoZG90cykgc3RheSB3aXRoaW4gdGhlIG1haW4gcmFuZ2UgZm9yIHRoZXNlIFFDIG1ldHJpY3MuCmBgYHtyfQpWbG5QbG90KHNyYXQsIGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiwgIm5Db3VudF9STkEiLCAicGVyY2VudC5tdCIpLCBuY29sID0gMykKClZsblBsb3Qoc3JhdCwgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiLCAibkNvdW50X1JOQSIsICJwZXJjZW50Lm10IiksIG5jb2wgPSAzLCBwdC5zaXplID0gMCkKClZsblBsb3Qoc3JhdCwgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiLCAibkNvdW50X1JOQSIpLCBuY29sID0gMiwgbG9nID0gVCkgIyB0aGVzZSB0d28gbWV0cmljcyBhcmUgYWxzbyB1c2VmdWwgaW4gbG9nIHNjYWxlCmBgYAoKQW5kIGFzIHNjYXR0ZXJwbG90cy4gVGhpcyB3b3JrcyBvbiBzbWFsbCBzYW1wbGVzIGJlY2F1c2UgdGhlcmUgYXJlIG5vdCB0aGF0IG1hbnkgcG9pbnRzLgoKRXhhbWluaW5nIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBudW1iZXIgb2YgQ291bnRzL1VNSSBhbmQgRmVhdHVyZXMgYWxzbyBzaG93cyB1cyBob3cgY2VsbHMgY2FuIGhhdmUgdGhlIHNhbWUgc2VxdWVuY2luZyBkZXB0aCAoVU1JKSBidXQgZXhwcmVzcyBtb3JlL2Zld2VyIGdlbmVzIChpLmUuIGEgYmlvbG9naWNhbCBxdWFudGl0eSkKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD01fQpwbG90MSA9IEZlYXR1cmVTY2F0dGVyKHNyYXQsIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCBmZWF0dXJlMiA9ICJwZXJjZW50Lm10IikKcGxvdDIgPSBGZWF0dXJlU2NhdHRlcihzcmF0LCBmZWF0dXJlMSA9ICJuQ291bnRfUk5BIiwgZmVhdHVyZTIgPSAibkZlYXR1cmVfUk5BIikKcGxvdDEgKyBwbG90MgpgYGAKCgpUaG9zZSBjZWxscyB3aXRoIGhpZ2ggcGVyY2VudC5tdCBhbHNvIHRlbmQgdG8gaGF2ZSBsb3cgcmVhZCBjb3VudHMsIGV4dHJhIGV2aWRlbmNlIHRoYXQgdGhlc2UgYXJlIGxvdyBxdWFsaXR5IGNlbGxzIHdoaWNoIHdpbGwgbm90IGJlIHVzZWZ1bCBmb3Igb3VyIGFuYWx5c2lzLgoKUmVhZCBjb3VudCBpcyB3ZWxsIGNvcnJlbGF0ZWQgd2l0aCBmZWF0dXJlIGNvdW50LiBUaGlzIGlzIHdoYXQgd2Ugd291bGQgZXhwZWN0LiBJZiB0aGVyZSB3ZXJlIGNlbGxzIHdpdGggbG90cyBvZiByZWFkcyBidXQgZmV3IGZlYXR1cmVzIHRoaXMgd291bGQgc3VnZ2VzdCBwcm9ibGVtcyB3aXRoIHRoZSBjYXB0dXJlIG9mIGRpdmVyc2UgUk5BIG1vbGVjdWxlcyBpbiB0aG9zZSBjZWxscy4KCiMgRmlsdGVyaW5nIGNlbGxzClRoZSBzaW1wbGVzdCB3YXkgdG8gZmlsdGVyIG91dCBjZWxscyBpcyB3aXRoIGhhcmQgY3V0b2ZmcyBhY3Jvc3MgYWxsIHNhbXBsZXMKCkJhc2VkIG9uIHRoaXMgZXhwbG9yYXRpb24sIHdlIHdpbGwgcGljayBzb21lIHRocmVzaG9sZHMgdG8gcmVtb3ZlIHRoZSBtb3N0IG91dGx5aW5nIGRyb3BsZXRzOiB3aXRoIGdyZWF0ZXIgdGhhbiA1MDAgYW5kIGxlc3MgdGhhbiA0MDAwIGRldGVjdGVkIGZlYXR1cmVzLCBhbmQgZ3JlYXRlciB0aGFuIDMwMDAgYW5kIGxlc3MgdGhhbiAzMDAwMCBVTUkgY291bnRzLiBUaGlzIGV4Y2x1ZGVzIHBvb3IgcXVhbGl0eSBjZWxscyB3aXRoIGxpdHRsZSBkYXRhIGFuZCBwb3RlbnRpYWwgZG91YmxldHMgd2hpY2ggaGF2ZSBtb3JlIGZlYXR1cmVzIHRoYW4gd2UgZXhwZWN0IHRvIHNlZSBpbiB0aGUgY2VsbHMuIFdlIHdpbGwgYWxzbyBleGNsdWRlIGRyb3BsZXRzIHdpdGggbGVzcyB0aGFuIDIwJSBtaXRvY2hvbmRyaWFsIHJlYWRzLgoKV2UgbWlnaHQgd2FudCB0byBjb21lIGJhY2sgYW5kIGFkanVzdCB0aGVzZSBjdXQgb2ZmcyBvbmNlIHdlIGhhdmUgc2VlbiB0aGUgVU1BUCBwbG90cy4gSXQgbWF5IGJlIHRoYXQgd2Ugc3RpbGwgZ2V0IGNsdXN0ZXJzIG9mIGxvdyBxdWFsaXR5IGNlbGxzIHRoYXQgc2hvdWxkIGhhdmUgYmVlbiByZW1vdmVkLiBCZXR0ZXIgdG8gYmUgY2F1dGlvdXMgdG8gYmVnaW4gd2l0aCwgdG8gYXZvaWQgZmlsdGVyaW5nIG91dCB1bnVzdWFsIGNlbGwgdHlwZXMuCgpgYGB7cn0KIyBkZWZpbmUgY29uZGl0aW9uIGZvciBmaWx0ZXJpbmcKY2VsbHNfdG9fZmlsdGVyIDwtIHJvd25hbWVzKHN1YnNldChzcmF0LCBzdWJzZXQgPSBuRmVhdHVyZV9STkEgPiA1MDAgJiBuRmVhdHVyZV9STkEgPCA0MDAwICYgbkNvdW50X1JOQSA+IDMwMDAgJiBuQ291bnRfUk5BIDwgMzAwMDAgJiAgcGVyY2VudC5tdCA8IDIwKUBtZXRhLmRhdGEpCiMgYWRkIHRoaXMgaW5mb3JtYXRpb24gdG8gdGhlIG1ldGFkYXRhCnNyYXQka2VlcCA8LSByb3duYW1lcyhzcmF0QG1ldGEuZGF0YSkgJWluJSBjZWxsc190b19maWx0ZXIKCiMjIHF1aWNrbHkgYWRkcmVzcyBob3cgbWFueSBjZWxscyB3ZSBhcmUga2VlcGluZwp0YWJsZShzcmF0JGtlZXApCmBgYApMZXTigJlzIGxvb2sgYmFjayBhdCBvdXIgZGlzdHJpYnV0aW9uIHBsb3RzLCBoaWdobGlnaHRpbmcgdGhlIGNlbGxzIHRoYXQgd2UgYXJlIHJldGFpbmluZyBhbmQgZmlsdGVyaW5nLgpgYGB7cn0KVmxuUGxvdChzcmF0LCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiwgInBlcmNlbnQubXQiKSwgbG9nPVRSVUUsIGdyb3VwLmJ5PSJrZWVwIikKCmBgYApPbmNlIHdlIGFyZSBoYXBweSB3aXRoIHRoZSBmaWx0ZXJpbmcsIHdlIGNhbiByZW1vdmUgdGhlIGxvdyBxdWFsaXR5IGNlbGxzIGZyb20gdGhlIFNldXJhdCBvYmplY3QuIE5vdGUgdGhhdCBmZWF0dXJlcyBoZXJlIGFyZSBnZW5lcyBhbmQgc2FtcGxlcyBhcmUgY2VsbHMuCmBgYHtyfQpzcmF0X2ZpbHQgPC0gc3Vic2V0KHNyYXQsIHN1YnNldCA9IGtlZXApCnNyYXRfZmlsdAoKIyMgb3RoZXIgb3B0aW9uIGZvciBmaWx0ZXJpbmcgYmFzZWQgb24gY2VsbCBuYW1lcwojc3JhdF9maWx0ID0gc3Vic2V0KHNyYXQsIGNlbGxzID0gY2VsbHNfdG9fZmlsdGVyKQoKYGBgCgpZb3Ugc2hvdWxkIHNhdmUgdGhlIGRhdGEgYXQgcmVndWxhciBpbnRlcnZhbHMgKGJ1dCB5b3UgZG9u4oCZdCBoYXZlIHRvIHJpZ2h0IG5vdykKYGBge3J9CiNzYXZlUkRTKHNyYXRfZmlsdCwgZmlsZSA9ICJFeGFtcGxlX2ZpbHRfc3JhdC5SRFMiKQpgYGAKCgoKIyBOb3JtYWxpc2F0aW9uCgojIyBMb2cgdHJhbnNmb3JtCgpOb3JtYWxpc2UgZXhwcmVzc2lvbiBvZiBlYWNoIGdlbmUgaW4gZWFjaCBjZWxsIGJ5IHRoZSB0b3RhbCBjb3VudHMgaW4gdGhhdCBjZWxsLCB3aXRoIGEgc2NhbGluZyBmYWN0b3Igb2YgMTAwMDAsIGFuZCB0aGVuIGxvZy10cmFuc2Zvcm0gdGhlIGRhdGEuIFRoaXMgaXMgZG9uZSB0byBzb21ld2hhdCBjb250cm9sIGV4cHJlc3Npb24gaW4gZWFjaCBjZWxsIGZvciB0aGVpciBzZXF1ZW5jaW5nIGRlcHRoIChhbHRob3VnaCB1c3VhbGx5IGl0J3Mgbm90IHN1ZmZpY2llbnQpLCBhcyB3ZWxsIGFzIGhhdmUgdGhlIGRhdGEgYWRvcHQgYSBtb3JlIEdhdXNzaWFuIGRpc3RyaWJ1dGlvbiAoc2luY2UgY291bnRzIGFyZSBOZWdhdGl2ZSBCaW5vbWlhbC9Qb2lzc29uIGRpc3RyaWJ1dGVkKSwgdG8gbWFrZSBpdCBtb3JlIGFtZW5hYmxlIHRvIG1ldGhvZHMgbGlrZSBQQ0EuCmBgYHtyfQpEZWZhdWx0QXNzYXkoc3JhdF9maWx0KSA9ICJSTkEiCnNyYXRfZmlsdCA9IE5vcm1hbGl6ZURhdGEoc3JhdF9maWx0LCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCBzY2FsZS5mYWN0b3IgPSAxMDAwMCkKYGBgClRoZW4sIHdlIGNhbiBmaW5kIHRoZSBoaWdobHkgdmFyaWFibGUgZ2VuZXMuIFRoZXNlIGFyZSBnZW5lcyB0aGF0IGFyZSB0aGUgbW9zdCBkaWZmZXJlbnQgYmV0d2VlbiBjZWxscywgYW5kIHRodXMgbW9yZSBsaWtlbHkgdG8gYmUgaW1wb3J0YW50IHRvIHJlZmxlY3QgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlbS4gVXNpbmcgSFZHIGNhbiBoZWxwIHRvIGhpZ2hsaWdodCBjZWxsIHBvcHVsYXRpb25zIGJ5IGZvY3VzaW5nIG9uIHRoZSBtb3N0IGRpZmZlcmVudCBnZW5lcywgYXMgd2VsbCBhcyByZWR1Y2Ugc29tZSBjb21wdXRhdGlvbiB0aW1lcyBzaW5jZSBmZXdlciBnZW5lcyBhcmUgYmVpbmcgY29uc2lkZXJlZC4gSG93ZXZlciwgZG9pbmcgdGhpcyBmaWx0ZXJpbmcgaXMgbm90IGFsd2F5cyBuZWNlc3NhcnkuCgpUaGVyZSBhcmUgbWFueSBwYXJhbWV0ZXJzIHRvIHNldCBhcyB0aHJlc2hvbGRzIGZvciB0aGlzLiBXZSBjYW4gZGVjaWRlIG9uIHdoYXQgdGhyZXNob2xkcyB0byB1c2UgdG8gZGVmaW5lIHRoZWlyIHZhcmlhYmlsaXR5LCBleHByZXNzaW9uIGxldmVsLCBldGMuOyBvciBvbiBob3cgbWFueSBnZW5lcyB3ZeKAmWQgbGlrZSB0byBnZXQgKGUuZy4gdG9wIDUwMDApLgpgYGB7cn0KIyB0b3AgNTAwMApzcmF0X2ZpbHQgPSBGaW5kVmFyaWFibGVGZWF0dXJlcyhzcmF0X2ZpbHQsIHNlbGVjdGlvbi5tZXRob2QgPSAidnN0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5mZWF0dXJlcyA9IDUwMDApCmBgYApgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTV9CiMgSWRlbnRpZnkgdGhlIDIwIG1vc3QgaGlnaGx5IHZhcmlhYmxlIGdlbmVzCnRvcDIwID0gaGVhZChWYXJpYWJsZUZlYXR1cmVzKHNyYXRfZmlsdCksIDIwKQoKIyBwbG90IHZhcmlhYmxlIGZlYXR1cmVzIHdpdGggYW5kIHdpdGhvdXQgbGFiZWxzCnBsb3QxID0gVmFyaWFibGVGZWF0dXJlUGxvdChzcmF0X2ZpbHQpCnBsb3QyID0gTGFiZWxQb2ludHMocGxvdCA9IHBsb3QxLCBwb2ludHMgPSB0b3AyMCwgcmVwZWwgPSBUUlVFKQpwbG90MgpgYGAKClRoZW4gd2Ugc2NhbGUgdGhlIGRhdGEuIFNjYWxlRGF0YSB3aWxsIHBlcmZvcm0gZmVhdHVyZShnZW5lKS1sZXZlbCBzY2FsaW5nLCBtZWFuaW5nIHRoYXQgZWFjaCBmZWF0dXJlIHdpbGwgYmUgY2VudGVyZWQgdG8gaGF2ZSBhIG1lYW4gb2YgMCBhbmQgc2NhbGVkIGJ5IHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgZWFjaCBmZWF0dXJlIChsaWtlIGhvdyBiYXNlIFIgc2NhbGUgd29ya3MpLiAKCkdvYWw6IFRvIG1ha2UgdGhlIGV4cHJlc3Npb24gb2YgYWxsIGdlbmVzIGNvbXBhcmFibGUgYnkgcmVtb3ZpbmcgdGVjaG5pY2FsIHZhcmlhdGlvbi4KSG93IGl0IHdvcmtzOgpGb3IgZWFjaCBnZW5lLCBjYWxjdWxhdGUgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBpdHMgZXhwcmVzc2lvbiBhY3Jvc3MgYWxsIGNlbGxzLgpGb3IgZWFjaCBjZWxsLCBzdWJ0cmFjdCB0aGUgZ2VuZSdzIG1lYW4gZXhwcmVzc2lvbiBmcm9tIGl0cyBleHByZXNzaW9uIHZhbHVlLgpEaXZpZGUgdGhlIHJlc3VsdCBieSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIHRvIGdldCBhIHNjYWxlZCB2YWx1ZS4KUmVzdWx0OiBIaWdobHkgZXhwcmVzc2VkIGdlbmVzIG5vIGxvbmdlciBkb21pbmF0ZSB0aGUgYW5hbHlzaXMsIGVuc3VyaW5nIGFsbCBnZW5lcyBoYXZlIGFuIGVxdWFsIGNoYW5jZSBvZiBiZWluZyBpbmZsdWVudGlhbC4gCgotIFNoaWZ0cyB0aGUgZXhwcmVzc2lvbiBvZiBlYWNoIGdlbmUsIHNvIHRoYXQgdGhlIG1lYW4gZXhwcmVzc2lvbiBhY3Jvc3MgY2VsbHMgaXMgMAotIFNjYWxlcyB0aGUgZXhwcmVzc2lvbiBvZiBlYWNoIGdlbmUsIHNvIHRoYXQgdGhlIHZhcmlhbmNlIGFjcm9zcyBjZWxscyBpcyAxCi0gVGhpcyBzdGVwIGdpdmVzIGVxdWFsIHdlaWdodCBpbiBkb3duc3RyZWFtIGFuYWx5c2VzLCBzbyB0aGF0IGhpZ2hseS1leHByZXNzZWQgZ2VuZXMgZG8gbm90IGRvbWluYXRlCi0gVGhlIHJlc3VsdHMgb2YgdGhpcyBhcmUgc3RvcmVkIGluIHBibWNbWyJSTkEiXV0kc2NhbGUuZGF0YQotIEJ5IGRlZmF1bHQsIG9ubHkgdmFyaWFibGUgZmVhdHVyZXMgYXJlIHNjYWxlZC4KLSBZb3UgY2FuIHNwZWNpZnkgdGhlIGZlYXR1cmVzIGFyZ3VtZW50IHRvIHNjYWxlIGFkZGl0aW9uYWwgZmVhdHVyZXMKV2XigJlyZSBhbHNvIHJlZ3Jlc3Npbmcgb3V0IHRoZSBlZmZlY3Qgb2YgdGhlIHRvdGFsIFVNSSBjb3VudHMgYW5kIG1pdG9jaG9uZHJpYWwgUk5BLiBUaGlzIGlzIHRvIGF2b2lkIGEgbGFyZ2UgYW1vdW50IG9mIHRoZSB2YXJpYW5jZSBiZWluZyBkdWUgdG8gZGlmZmVyZW5jZXMgaW4gcmVhZCBjb3VudHMgYmV0d2VlbiBjZWxscy4gWW91IGNhbiBhZGQgaGVyZSBhbnkgc291cmNlIG9mIHVud2FudGVkIHZhcmlhdGlvbgpgYGB7cn0Kc3JhdF9maWx0ID0gU2NhbGVEYXRhKHNyYXRfZmlsdCwgdmFycy50by5yZWdyZXNzID0gYygibkNvdW50X1JOQSIsICJwZXJjZW50Lm10IiksIAogICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFQpCmBgYAoKCiMjIFNDVHJhbnNmb3JtCgpBbm90aGVyIG5vcm1hbGlzYXRpb24gbWV0aG9kIGlzIFNDVHJhbnNmb3JtLiBUaGlzIG1ldGhvZCB1c2VzIFBlYXJzb24gcmVzaWR1YWxzIGZvciBub3JtYWxpc2F0aW9uLCBhbmQgeW91IGNhbiByZWFkIG1vcmUgYWJvdXQgaXQgaGVyZTogPGh0dHBzOi8vZ2Vub21lYmlvbG9neS5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2L3MxMzA1OS0wMTktMTg3NC0xPi4gUnVubmluZyB0aGlzIHdpbGwgYWxzbyBhdXRvbWF0aWNhbGx5IHRlbGwgdXMgd2hhdCBhcmUgdGhlIEhWRy4gQW4gdXBkYXRlZCB0dXRvcmlhbCBmb3IgU0NUcmFuc2Zvcm0gVjIgY2FuIGJlIGZvdW5kIGhlcmU6IDxodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FyY2hpdmUvdjQuMy9zY3RyYW5zZm9ybV92Ml92aWduZXR0ZT4uCgpJbXBvcnRhbnRseSwgaW4gU2V1cmF0LCB0aGUgcmVzdWx0IG9mIGVhY2ggb2YgdGhlc2Ugbm9ybWFsaXNhdGlvbnMgY2FuIGJlIHN0b3JlZCBpbiBhIGRpZmZlcmVudCBhc3NheSAtIHRoZSBtb3JlIGNvbW1vbiBtZXRob2QgaW4gdGhlIFJOQSBhc3NheSwgdGhlIHNjVHJhbnNmb3JtIG1ldGhvZCBpbiBTQ1QuCgpgYGB7cn0Kc3JhdF9maWx0ID0gU0NUcmFuc2Zvcm0oc3JhdF9maWx0LCB2ZXJib3NlID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgdmFycy50by5yZWdyZXNzID0gYygibkNvdW50X1JOQSIsICJwZXJjZW50Lm10IiksICAKICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMubiA9IDUwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQudXNlID0gMTIzKQpEZWZhdWx0QXNzYXkoc3JhdF9maWx0KSA9ICJTQ1QiCmBgYAoKV2UgY2FuIGNvbXBhcmUgdGhlIGludGVyc2VjdGlvbiBvZiBoaWdobHkgdmFyaWFibGUgZ2VuZXMKYGBge3J9Cmxpc3RfaHZnID0gbGlzdCgiUk5BIiA9IFZhcmlhYmxlRmVhdHVyZXMoc3JhdF9maWx0LCBhc3NheSA9ICJSTkEiKSwgCiAgICAgICAgICAgICAgICAiU0NUIiA9IFZhcmlhYmxlRmVhdHVyZXMoc3JhdF9maWx0LCBhc3NheSA9ICJTQ1QiKSkKZ3Bsb3RzOjp2ZW5uKGxpc3RfaHZnKQpgYGAKCiMgRGltZW5zaW9uYWxpdHkgUmVkdWN0aW9uIAoKc2NSTkEtc2VxIGRhdGFzZXRzIGNvbW1vbmx5IGhhdmUgfjEwLjAwMCBkYXRhIHBvaW50cyAoY2VsbHMpIGFuZCB+MjAuMDAwIGZlYXR1cmVzIChnZW5lcykuIFRoaXMgaXMgb2YgY291cnNlIHZlcnkgaGFyZCB0byB2aXN1YWxpc2UhIERSIGFsZ29yaXRobXMgaGVscCB1cyB2aXN1YWxpemUgdGhlIG1ham9yIHZhcmlhYmlsaXR5IHdpdGhpbiBhIGRhdGFzZXQuCgojIyBQQ0EKUENBIGlzIHRoZSBtb3N0IGNvbW1vbiBsaW5lYXIgRFIgbWV0aG9kIHVzZWQgaW4gc3RhdGlzdGljcy4gSXQg4oCcY29tcHJlc3Nlc+KAnSB0aGUgZGF0YSBpbnRvIGEgc2V0IG9mIHVuY29ycmVsYXRlZCBkaW1lbnNpb25zIChwcmluY2lwYWwgY29tcG9uZW50cyksIHdoaWxlIGFsc28gZGV0ZXJtaW5pbmcgdGhlIHdlaWdodHMgZm9yIHRoZSBmZWF0dXJlcyAoZ2VuZXMpIG9mIGVhY2ggUEMuCldlIGRvIGxpbmVhciBkaW1lbnNpb24gcmVkdWN0aW9uIChQQ0EpIHRvIGRldGVybWluZSB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMgb2YgdmFyaWF0aW9uIGluIHRoZSBkYXRhLiBOb3RlIHRoZSBnZW5lcyBhc3NvY2lhdGVkIHdpdGggdGhlIGZpcnN0IDUgUENzLiBJcyB0aGVyZSBhbnl0aGluZyBub3RpY2VhYmxlIGFib3V0IHRoZW0/IElmIHRoZXJlIGFyZSByZWNvZ25pc2FibGUgZ3JvdXBzIG9mIGdlbmVzIGFzc29jaWF0ZWQgd2l0aCBhIHBhcnRpY3VsYXIgUEMgdGhpcyBsaWtlbHkgcmVwcmVzZW50cyBhIHN0cm9uZyBiaW9sb2dpY2FsIHNpZ25hbHMgKHRoZSBtb3N0IGRpc3RpbmN0IGNlbGwgdHlwZSwgdmFyaWF0aW9uIGluIGNlbGwgY3ljbGUgc3RhZ2UsIG9yIGEgdGVjaG5pY2FsIHByb2JsZW0gKGxvdyBxdWFsaXR5IGNlbGxzKS4KQWxzbyBvZiBub3RlLCB3ZSB3aWxsIGtlZXAgd29ya2luZyB3aXRoIHRoZSBTQ1QgYXNzYXkgLSBub3RlIHRoZSDigJxhc3NheeKAnSBhcmd1bWVudC4KCmBgYHtyfQpzcmF0X2ZpbHQgPSBSdW5QQ0Eoc3JhdF9maWx0LCB2ZXJib3NlID0gVCwgYXNzYXkgPSAiU0NUIiwgbnBjcyA9IDUwKQpgYGAKVGhlIHJlc3VsdCBvZiB0aGUgUENBIGlzIHN0b3JlZCBpbiB0aGUgcmVkdWN0aW9ucyBzbG90LCBoZXJlIGFzICRwY2E6CmBgYHtyfQpzcmF0X2ZpbHRAcmVkdWN0aW9ucwoKYGBgCldlIGNhbiBwbG90IHRoZSBmaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMgdXNpbmcgdGhlIERpbVBsb3QoKSBmdW5jdGlvbi4KYGBge3J9CkRpbVBsb3Qoc3JhdF9maWx0LCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MikKCmBgYApMb29raW5nIGF0IHRoZSBsb2FkaW5ncyAoaS5lLiBnZW5lIGNvbnRyaWJ1dGlvbnMpIG9mIGVhY2ggUEMgY2FuIGFscmVhZHkgdGVsbCB1cyBhIGZldyB0aGluZ3MgYWJvdXQgdGhlIGJpb2xvZ2ljYWwgdmFyaWFiaWxpdHkgaW4gdGhlIHNhbXBsZQpgYGB7ciwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9Nn0KVml6RGltTG9hZGluZ3Moc3JhdF9maWx0LCBkaW1zID0gMTozLCByZWR1Y3Rpb24gPSAicGNhIiwgbmNvbCA9IDMpCmBgYApIZXJlIHNpbWlsYXIsIGJ1dCBtb3JlIFBDcyBhbmQgcGxvdHRpbmcgZXhwcmVzc2lvbiBvZiB0aGUgdG9wIDUwMCBjZWxscyBpbiBlYWNoIFBDLCBhcyB3ZWxsIGFzIHRoZSB0b3AgZ2VuZXMKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTEwfQpEaW1IZWF0bWFwKHNyYXRfZmlsdCwgcmVkdWN0aW9uID0gInBjYSIsIGFzc2F5cyA9ICJTQ1QiLCBkaW1zID0gMToxMiwgCiAgICAgICAgICAgY2VsbHMgPSA1MDAsIGJhbGFuY2VkID0gVFJVRSwgbmNvbCA9IDMpCmBgYAoKV2UgbmVlZCB0byBwaWNrIGEgbnVtYmVyIG9mIFBDcyB3aGljaCBjYXB0dXJlIG1vc3Qgb2YgdGhlIHZhcmlhbmNlIGluIHRoZSBkYXRhLiBUaGVzZSB3aWxsIGJlIHVzZWQgZm9yIGRvd25zdHJlYW0gYW5hbHlzaXMuIEhvdyBtYW55IFBDcyBzaG91bGQgd2UgdXNlIHRvIGRlc2NyaWJlIHRoZSBkYXRhPyBUaGlzIHBsb3Qgc2hvd3MgaG93IG11Y2ggdmFyaWFuY2UgaXMgY2FwdHVyZWQgYnkgZWFjaCBQQy4gClRoaXMgaXMgbW9zdGx5IGFuIOKAnGV5ZWJhbGxpbmfigJ0gbWV0aG9kLCB0byBzZWxlY3QgYXBwcm94aW1hdGVseSB3aGVuIHRoZSB2YXJpYW5jZSBleHBsYWluZWQgcGVyIFBDIGZsYXR0ZW5zLCB3aGljaCB3b3VsZCBpbmRpY2F0ZSB0aGF0IG1vc3Qgb2YgdGhlIGRhdGFzZXRzIHZhcmlhYmlsaXR5IGlzIHByZXNlbnQgaW4gdGhvc2UgdG9wIFBDcy4gSXQgaXMgYWxzbyByZWxldmFudCB0byBub3RlIHRoYXQsIHNob3VsZCBhbnkgUEMgcmVmbGVjdCB1bndhbnRlZCB2YXJpYWJpbGl0eSAoZS5nLiBzZXF1ZW5jaW5nIGRlcHRoIHBlciBjZWxsLCBvciBvdGhlciB0ZWNobmljYWwgZmFjdG9ycykgaXQgY2FuIGJlIGV4Y2x1ZGVkLgpgYGB7cn0KRWxib3dQbG90KHNyYXRfZmlsdCwgbmRpbXMgPSA1MCkKYGBgCkEgbGFyZ2UgcHJvcG9ydGlvbiBvZiB0aGUgdmFyaWF0aW9uIGlzIGNhcHR1cmVkIGJ5IDE1IGNvbXBvbmVudHMgYW5kIHRoZXJlIGlzIGEgZHJvcCBvZmYgdGhlcmVhZnRlci4gTGV04oCZcyBnbyB3aXRoIDIwLgpgYGB7cn0KbmNvbXAgPSAyMApgYGAKCgojIE5vbi1MaW5lYXIgRGltZW5zaW9uYWxpdHkgUmVkdWN0aW9uIAoKTm9uLWxpbmVhciBkaW1lbnNpb24gcmVkdWN0aW9uIG1ldGhvZHMgc3VjaCBhcyBVTUFQIGFuZCBUU05FIHRha2UgdGhlIFBDQSBkYXRhIGFzIGEgc3RhcnRpbmcgcG9pbnQsIGJ1dCBhcmUgYWJsZSB0byB0YWtlIG1vcmUgY29tcGxleCAobm9uLWxpbmVhcikgcGF0dGVybnMgaGlkZGVuIGluIHRoZSBkYXRhIGFuZCByZXByZXNlbnQgdGhlbSBpbiBvbmx5IHR3byBkaW1lbnNpb25zICh3aGljaCBodW1hbnMgYXJlIGdvb2QgYXQgZXhhbWluaW5nKS5UaGVzZSBtZXRob2RzIHdpbGwgdGFrZSB0aGUgbWFpbiBQQ3MgZGV0ZXJtaW5lZCBhYm92ZSBhbmQgcHJvamVjdCB0aGVtIGluIGEgbG93ZXIgZGltZW5zaW9uIHNwYWNlLCBhdCB0aGUgY29zdCBvZiBkaXN0b3J0aW5nIGRpc3RhbmNlcyBiZXR3ZWVuIHBvaW50cyB0byBmb3JtIG9yZ2FuaXNlZCBjbHVtcHMuIFRodXMsIHRoZSBvdXRwdXQgb2YgdGhlc2UgbWV0aG9kcyBzaG91bGQgb25seSBiZSB1c2VkIGZvciB2aXN1YWxpc2F0aW9uLCBhcyBhIGdlbmVyYWwgZ3VpZGUgZm9yIHdoYXQgdGhlIGRhdGEg4oCcbG9va3PigJ0gbGlrZS4KYGBge3J9CnNyYXRfZmlsdCA8LSBSdW5VTUFQKHNyYXRfZmlsdCwgZGltcyA9IDE6bmNvbXAsIHZlcmJvc2UgPSBGKQoKYGBgClBsb3QgdGhlIHByb2plY3Rpb24KYGBge3J9CkRpbVBsb3Qoc3JhdF9maWx0LCByZWR1Y3Rpb24gPSAidW1hcCIpCgpgYGAKQmVhciBpbiBtaW5kIC0gZG8gbm90IG92ZXJpbnRlcnByZXQgdGhlc2UgcGxvdHMhIHRTTkUgYW5kIFVNQVAgc2VydmUgYXMgc2ltcGxlIHdheXMgdG8gcmVwcmVzZW50IHlvdXIgZGF0YeKAmXMgdmFyaWFiaWxpdHkgaW4gMkQsIGFuZCBhbGxvdyBmb3IgZXllYmFsbGluZyB0aGUgYXBwcm94aW1hdGUgbnVtYmVyIG9mIGNlbGwgcG9wdWxhdGlvbnMgaW4geW91ciBkYXRhLiBIb3dldmVyLCBvbmUgY2Fubm90IHRlbGwgaG93IGRpZmZlcmVudCBjZWxsIHBvcHVsYXRpb25zIGFyZSBmcm9tIGVhY2ggb3RoZXIgYmFzZWQgb24gdGhlaXIgZGlzdGFuY2VzLCBvciBldmVuIGhvdyBtYW55IOKAnHJlYWzigJ0gcG9wdWxhdGlvbnMgZXhpc3QgLSB0aGlzIGNvbWVzIGRvd24gdG8gaW50ZXJwcmV0YXRpb24uCgpJZiB3ZSBwbG90IHRoZSBleHByZXNzaW9uIG9mIHRoZSBCIGNlbGwgbWFya2VyIENENzlBIChiZWxvdyksIHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBpcyBhIGNsZWFyIGNsdXN0ZXIgb2YgQiBjZWxscwpgYGB7cn0KRmVhdHVyZVBsb3Qoc3JhdF9maWx0LCByZWR1Y3Rpb24gPSAidW1hcCIsIGZlYXR1cmVzID0gYygnQ0Q3OUEnKSkKCmBgYAojIENsdXN0ZXJpbmcKV2UgY2FuIHVzZSB0aGUgY2hvc2VuIFBDcyB0byBnZXQgYSBuZWlnaGJvcmhvb2QgZ3JhcGggZm9yIGFsbCBjZWxscy4gVGhpcyBncmFwaCBpcyBkb25lIGluIFBDIHNwYWNlLCBkZXRlcm1pbmluZyB3aGljaCBjZWxscyBhcmUgbW9yZSBzaW1pbGFyIHRvIHdoaWNoIGJ5IHRoZWlyIGRpc3RhbmNlcyBpbiB0aGUgY2hvc2VuIHRvcCBQQ3MuCmBgYHtyfQpyZWQgPSAicGNhIgpzcmF0X2ZpbHQgPSBGaW5kTmVpZ2hib3JzKHNyYXRfZmlsdCwgZGltcyA9IDE6bmNvbXAsIHZlcmJvc2UgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IHJlZCwgZ3JhcGgubmFtZSA9IHBhc3RlMChyZWQsIG5jb21wKSkKYGBgClRoaXMgYWxsb3dzIHVzIHRvIGRldGVjdCBjbHVzdGVycyBvZiBjZWxscyB0byB0aGVuIGJlIGludGVycHJldGVkLiBGb3IgdGhpcyB3ZSBjYW4gdXNlIGNvbW11bml0eSBkZXRlY3Rpb24gYWxnb3JpdGhtcyAtIG1ldGhvZHMgdG8gZGlzY292ZXIgYXNzb2NpYXRlZCBncm91cHMgb2YgcG9pbnRzIGluIGdyYXBocy4gV2XigJlyZSB1c2luZyBhbGdvcml0aG0gMiAoTG91dmFpbiBhbGdvcml0aG0gd2l0aCBtdWx0aWxldmVsIHJlZmluZW1lbnQpLCBhbmQgd2XigJlyZSBnZXR0aW5nIGNsdXN0ZXJzIGZvciBtdWx0aXBsZSByZXNvbHV0aW9ucy4gQSBoaWdoZXIgcmVzb2x1dGlvbiBjYW4gcmV0dXJuIG1vcmUgY2x1c3RlcnMuIExlaWRlbiBjbHVzdGVyaW5nIChhbm90aGVyIGFsZ29yaXRobSkgbWlnaHQgY3VycmVudGx5IGJlIHRoZSBiZXN0IGNob2ljZSwgYnV0IGl04oCZcyBtb3JlIGRpZmZpY3VsdCB0byBpbnN0YWxsLgpgYGB7cn0Kc3JhdF9maWx0ID0gRmluZENsdXN0ZXJzKHNyYXRfZmlsdCwgYWxnb3JpdGhtID0gMiwgdmVyYm9zZSA9IFQsIAogICAgICAgICAgICAgICAgICAgICAgICAgZ3JhcGgubmFtZSA9IHBhc3RlMChyZWQsIG5jb21wKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdXRpb24gPSBzZXEoMC4yLCAxLCAwLjEpKQpgYGAKYGBge3J9CmhlYWQoc3JhdF9maWx0QG1ldGEuZGF0YSkKCmBgYAoKQW5kIHNvIHdlIGNhbiBoYXZlIGEgbG9vayBhdCBhbGwgY2x1c3RlcmluZyByZXNvbHV0aW9ucwpgYGB7ciwgIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KcGx0X2xpc3QgPSBsaXN0KCkKIyBpdGVyYXRlIGVhY2ggY2x1c3RlcmluZyByZXNvbHV0aW9uCmZvcihjbCBpbiBjb2xuYW1lcyhzcmF0X2ZpbHRAbWV0YS5kYXRhKVtncmVwbChwYXN0ZTAoInBjYSIsIG5jb21wLCAiXyIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG5hbWVzKHNyYXRfZmlsdEBtZXRhLmRhdGEpKV0pewpzcmF0X2ZpbHQgPSBTZXRJZGVudChzcmF0X2ZpbHQsIHZhbHVlID0gY2wpICMgc2V0IHRoZSByZXNvbHV0aW9uIGFzIGRlZmF1bHQgaWRlbnRpdHkKcGx0X2xpc3RbW2NsXV0gPSBEaW1QbG90KHNyYXRfZmlsdCwgcmVkdWN0aW9uID0gInVtYXAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gVCwgcmFzdGVyID0gRiwgcHQuc2l6ZSA9IDAuMikrCiAgICBsYWJzKHN1YnRpdGxlID0gY2wpKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgYXNwZWN0LnJhdGlvID0gMSkKfQpjb3dwbG90OjpwbG90X2dyaWQocGxvdGxpc3QgPSBwbHRfbGlzdCwgbmNvbCA9IDMpCgoKYGBgClVNQVAvdFNORSBjYW4gYmUgYSBnb29kIHZpc3VhbCBndWlkZSBmb3IgaG93IG1hbnkgY2x1c3RlcnMgY2FuIGJlIGV4cGVjdGVkLiBBbHRlcm5hdGl2ZWx5LCBvbmUgY2FuIHVzZSBhIHBhY2thZ2Ugc3VjaCBhcyBjbHVzdHJlZSB0byBjaG9vc2UgYSBjbHVzdGVyaW5nIHJlc29sdXRpb24uIFJlc29sdXRpb24gY2FuIGJlIGNob3NlbiBhcyB0aGUgb25lIGJlZm9yZSBjbHVzdGVycyBzdGFydCBiZWNvbWluZyB1bnN0YWJsZS4KYGBge3IsIGZpZy5oZWlnaHQgPSAxMCwgZmlnLndpZHRoPTh9CmNsdXN0cmVlOjpjbHVzdHJlZShzcmF0X2ZpbHQsIHByZWZpeCA9IHBhc3RlMCgicGNhIiwgbmNvbXAsICJfcmVzLiIpLCBub2RlX2NvbG91ciA9ICJzYzNfc3RhYmlsaXR5IikKCmBgYApUaGVuIHdlIGRlZmluZSB0aGF0IHJlc29sdXRpb24gYXMgdGhlIGRlZmF1bHQgaWRlbnRpdHkKYGBge3J9CmNsX3VzZSA9IHBhc3RlMChyZWQsIG5jb21wLCAiX3Jlcy4iLCAwLjgpCnNyYXRfZmlsdCA9IFNldElkZW50KHNyYXRfZmlsdCwgdmFsdWUgPSBjbF91c2UpCmBgYAoKIyBDZWxsIHR5cGUgaWRlbnRpZmljYXRpb24KV2UgY2FuIG5vdyBzdGFydCB0byBhc2sg4oCcd2hpY2ggb2YgdGhlc2UgY2x1c3RlcnMgYXJlIHJlYWwgY2VsbCBwb3B1bGF0aW9ucz/igJ0uIEFuZCBieSByZWFsLCB3ZSBtZWFuIOKAnGJpb2xvZ2ljYWxseSByZWxldmFudOKAnSwgaS5lLiB0aGV5IHJlcHJlc2VudCBhIGNlbGwgdHlwZS9zdGF0ZSBpbiBvdXIgc3lzdGVtIGJlaW5nIHN0dWRpZWQuCgpTaW5jZSB0aGUgVU1BUCBwbG90IGlzIGdlbmVyYXRlZCBiYXNlZCBvbiBhIFBDQSB0aGF0IHNob3VsZCByZWZsZWN0IHNvbWUgcmVjb2duaXplZCBiaW9sb2dpY2FsIHZhcmlhYmlsaXR5LCB3ZSBjYW4gZXhwZWN0IHRoYXQgdGhlIOKAnGNsdW1wc+KAnSB0aGF0IGl0IGZvcm1zIGFyZSB0byBzb21lIGRlZ3JlZSByZWxhdGVkIHRvIHRoaXMuIFNvIG9uZSBzdHJhdGVneSBpcyB0byBwaWNrIGEgY2x1c3RlcmluZyByZXNvbHV0aW9uIHRoYXQgY29pbmNpZGVzIHdpdGggbW9zdCBvZiB0aGVzZSBwZXJjZWl2ZWQgY2x1bXBzLgoKQW5vdGhlciBzZW5zaWJsZSBhcHByb2FjaCBpcyB0byB1c2UgbWFya2VyIGdlbmVzIGZvciBleHBlY3RlZCBjZWxsIHBvcHVsYXRpb25zIGluIG91ciBkYXRhLiBXZSBjYW4gY2hlY2sgZm9yIHRoZWlyIGV4cHJlc3Npb24gdG8gaGF2ZSBhbiBhcHByb3hpbWF0ZSBmZWVsaW5nIG9mIHdoYXQgZWFjaCBjbHVzdGVyIG9yIGdyb3VwIG9mIGNsdXN0ZXJzIGlzIGJlZm9yZSB3ZSBnZXQgdGhlaXIgbWFya2VyIGdlbmVzCgpXZSBoYXZlIG91ciBjbHVzdGVycywgbm93IGxldOKAmXMgbG9vayBhdCB3aGVyZSBzb21lIG1hcmtlcnMgb2YgaW50ZXJlc3QgYXJlIGV4cHJlc3NlZC4gQmFzZWQgb24gcHVibGlzaGVkIGRhdGEsIHdlIGtub3cgdGhhdDoKCi0gQiBjZWxscyBleHByZXNzIENENzlBCi0gTW9ub2N5dGVzIGV4cHJlc3MgQ1NUMwotIFQgY2VsbHMgZXhwcmVzcyBDRDNECgpgYGB7cn0KIyBQbG90IHRoZSBleHByZXNzaW9uIHZhbHVlcyBvZiBlYWNoIG1hcmtlcnMgb24gdGhlIFVNQVAKRmVhdHVyZVBsb3Qoc3JhdF9maWx0LCBmZWF0dXJlcyA9IGMoIkNENzlBIiwgIkNTVDMiLCAiQ0QzRCIpLCBwdC5zaXplID0gMC4xLCBsYWJlbCA9IFRSVUUsIHJlZHVjdGlvbiA9ICd1bWFwJykKYGBgCgpBcyB2aW9saW4gcGxvdApgYGB7ciwgZmlnLndpZHRoPTh9ClZsblBsb3Qoc3JhdF9maWx0LCBmZWF0dXJlcyAgPSBjKCJDRDc5QSIsICJDU1QzIiwgIkNEM0QiKSkKYGBgCgotIENsdXN0ZXJzIDQgYW5kIDYgc2VlbSBsaWtlIGJlIEIgY2VsbHMgCi0gQ2x1c3RlcnMgMSwgMyBhbmQgOSBzZWVtIGxpa2UgbW9ub2N5dGVzLgotIENsdXN0ZXJzIDAsIDIsIDUgYW5kIDggc2VlbSBsaWtlIFQgY2VsbHMKCldoYXQgYWJvdXQgNz8gV2hhdCBhcmUgdGhlIGRpZmZlcmVudCBjdXN0ZXJzIHdpdGhpbiBlYWNoIGNlbGwgdHlwZS4gQXJlIHdlIHN1cmUgdGhleSBhcmUgY29ycmVjdGx5IGFzc2lnbmVkPwoKCldlIGNhbiBjYWxjdWxhdGUgdGhlIG1hcmtlciBnZW5lcyBmb3IgYSBjaG9zZW4gY2x1c3RlcmluZyByZXNvbHV0aW9uLiBUaGlzIGlzIGRvbmUgYXMgYSAxIHZzIHJlc3QgY29tcGFyaXNvbi4KCkJlY2F1c2Ugb2YgdGhlIG5hdHVyZSBvZiBsYXJnZSBzYW1wbGUgc2l6ZSBpbiBzY1JOQS1zZXEgZGF0YSAob25lIGNlbGwgaXMgb25lIHNhbXBsZSksIGl0IGlzIHN0cm9uZ2x5IHJlY29tbWVuZGVkIHRvIG5vdCBvbmx5IGxvb2sgYXQgcC12YWx1ZXMsIGJ1dCBhbHNvIGRldGVjdGlvbiByYXRlIG9mIHRoZSBnZW5lIGluIHRoZSBjbHVzdGVyIChwY3QpIGFuZCBmb2xkIGNoYW5nZSAobG9nRkMpIGJldHdlZW4gY2VsbHMgaW4gYW5kIG91dHNpZGUgdGhlIGNsdXN0ZXIuCmBgYHtyfQpzcmF0X2ZpbHQgPSBTZXRJZGVudChzcmF0X2ZpbHQsIHZhbHVlID0gY2xfdXNlKQoKbWtfY2x1c3RlcnMgPSBGaW5kQWxsTWFya2VycyhzcmF0X2ZpbHQsIGFzc2F5ID0gIlNDVCIsIHRlc3QudXNlID0gIndpbGNveCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nZmMudGhyZXNob2xkID0gMC4yLCB2ZXJib3NlID0gVCwgb25seS5wb3MgPSBUKQoKCgpta190b3AgPSBta19jbHVzdGVycyAlPiUgCiAgZ3JvdXBfYnkoY2x1c3RlcikgICU+JSAgIyBmb3IgZWFjaCBjbHVzdGVyCiAgZmlsdGVyKHBfdmFsX2Fkajw9MC4wNSkgICU+JSAgIyBvbmx5IHAtdmFsdWUgYmVsb3cgMC4wNQogIHRvcF9uKG4gPSAyMCwgd3QgPSBhdmdfbG9nMkZDKSAjIHRvcCBnZW5lcyBwZXIgY2x1c3RlciwgcmFua2VkIGJ5IGxvZ0ZDCgpta190b3AKYGBgCgpXZSBjYW4gYWxzbyBkaXJlY3RseSBjb21wYXJlIHBhaXJzIG9mIGNlbGwgcG9wdWxhdGlvbnMgKDEgdnMgMSBjb21wYXJpc29uKS4KCgpgYGB7cn0KbWtfY29tcCA9IEZpbmRNYXJrZXJzKHNyYXRfZmlsdCwgaWRlbnQuMSA9ICIyIiwgaWRlbnQuMiA9ICI1IiwgYXNzYXkgPSAiU0NUIiwgCiAgICAgICAgICAgICAgICAgICB0ZXN0LnVzZSA9ICJ3aWxjb3giLCBsb2dmYy50aHJlc2hvbGQgPSAwLjIpCmBgYAoKCgojIEFzc2lnbmluZyBjZWxsIHR5cGUgaWRlbnRpdHkgdG8gY2x1c3RlcnMKCkZvcnR1bmF0ZWx5IGluIHRoZSBjYXNlIG9mIHRoaXMgZGF0YXNldCwgd2UgY2FuIHVzZSBjYW5vbmljYWwgbWFya2VycyB0byBlYXNpbHkgbWF0Y2ggdGhlIHVuYmlhc2VkIGNsdXN0ZXJpbmcgdG8ga25vd24gY2VsbCB0eXBlcy4gQXppbXV0aCBpcyBhIGdvb2QgcmVmZXJlbmNlIGZvciBtYXJrZXIgZ2VuZXMsIHdpdGggZGlmZmVyZW50IGxldmVscyBvZiAiZ3JhbnVsYXJpdHkiIDxodHRwczovL2F6aW11dGguaHVibWFwY29uc29ydGl1bS5vcmcvcmVmZXJlbmNlcy8jSHVtYW4lMjAtJTIwUEJNQz4KCi0gKipFeGVyY2lzZToqKiBiYXNlZCBvbiB0aGUgY2VsbCBtYXJrZXJzIGZyb20gZWFjaCBjbHVzdGVyLCB0cnkgdG8gYW5ub3RhdGUgdGhlbSB0byBjZWxsIHR5cGVzLiAKCi0gKipCb251czoqKiB5b3UgY2FuIGFkZCB0aGlzIGluZm9ybWF0aW9uIHRvIHlvdXIgbWV0YS5kYXRhLCBhbmQgdXNlIGl0IGZvciBwbG90dGluZy4KCkludmVzdGlnYXRlIHRoZSBtYXJrZXJzLCBhcmUgdGhleSB3aWRlc3ByZWFkIHdpdGhpbiB0aGUgY2x1c3Rlcj8gT3Igb25seSBleHByZXNzZWQgaW4gZmV3IGNlbGxzPyAKCkNlbGwgdHlwZSBpZGVudGlmaWNhdGlvbi9hbm5vdGF0aW9uIGlzIG9uZSBvZiB0aGUgaGFyZGVzdCBhbmQgbW9zdCBsYWJvcmlvdXMgdGFza3MgaW4gc2NSTkFzZXEgYW5hbHlzaXMuIFlvdSB3aWxsIG5lZWQgdG8gcGxheSB3aXRoIGNsdXN0ZXJpbmcgcmVzb2x1dGlvbnMsIHN1Yi1jbHVzdGVyaW5nLCBhbmQgbG9vayBmb3IgbWFya2VycyBpbiBsb3RzIG9mIHBhcGVycyBhbmQgZGF0YWJhc2VzIHRvIGFubm90YXRlIHlvdXIgY2VsbHMgKGFsd2F5cyBkZXBlbmRpbmcgb24gdGhlIGxldmVsIG9mIGRldGFpbCB5b3Ugd2lzaCB0byBhY2hpZXZlKS4gTW9yZW92ZXIsIGF2YWlsYWJsZSBtZXRob2RzIGV4aXN0IHRvIGF1dG9tYXRpY2FsbHkgY2xhc3NpZnkgY2VsbHMgaW50byBjZWxsIHR5cGVzIGJhc2VkIG9uIHByZXZpb3VzbHkgYW5ub3RhdGVkIGRhdGEuIFNvbWUgb2YgdGhvc2UgcmVzb3VyY2VzIGFyZSBsaXN0ZWQgaGVyZTogPGh0dHBzOi8vd3d3LjEweGdlbm9taWNzLmNvbS9hbmFseXNpcy1ndWlkZXMvd2ViLXJlc291cmNlcy1mb3ItY2VsbC10eXBlLWFubm90YXRpb24+CgoKCkl0IG9mdGVuIGhhcHBlbnMgdGhhdCBhbmFseXNpbmcgdGhlIHRvdGFsIHNhbXBsZSBkb2VzIG5vdCBwcm92aWRlIGEgY29oZXJlbnQgcGljdHVyZSBvZiBhbGwgc3VicG9wdWxhdGlvbnMgcHJlc2VudC4gVGhpcyBjYW4gaGFwcGVuIGJlY2F1c2UgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBjZXJ0YWluIGNlbGwgc3VidHlwZXMgbWlnaHQgbm90IGJlIGxhcmdlIGVub3VnaCB0byBkZXRlY3QgdGhlbSwgY29tcGFyZWQgdG8gbW9yZSBkaXN0aW5jdCBwb3B1bGF0aW9ucy4KClNldXJhdCBpbmNsdWRlcyB0aGUgZnVuY3Rpb24gYmVsb3cgdG8gZWFzaWx5IG9idGFpbiBzdWJjbHVzdGVycyBmcm9tIHNwZWNpZmljIGNsdXN0ZXJzLiBIb3dldmVyLCBvdXIgYWR2aWNlIGlzIGZvciB5b3UgdG8gbm90IHNpbXBseSBzdWJzZXQgYW5kIGNsdXN0ZXIsIGJ1dCByYXRoZXIgcmVjYWxjdWxhdGUgSFZHLCBQQ0EsIGFuZCBOTiBncmFwaCBmb3IgdGhlIGNlbGwgc3Vic2V0LiBUaGlzIGlzIGJlY2F1c2Ugd2UgbWlnaHQgZmluZCB0aGF0IHRoZSBpbml0aWFsIHNldCBvZiBIVkcvUENzIGRvZXMgbm90IHJlZmxlY3QgdGhlaXIgdmFyaWFiaWxpdHkuCmBgYHtyfQpzcmF0X2ZpbHQgPSBGaW5kU3ViQ2x1c3RlcihzcmF0X2ZpbHQsIGNsdXN0ZXIgPSAiMCIsIGdyYXBoLm5hbWUgPSBwYXN0ZTAocmVkLCBuY29tcCkpCnRhYmxlKHNyYXRfZmlsdEBtZXRhLmRhdGEkc3ViLmNsdXN0ZXIpCmBgYAoKU2V1cmF0IGFsc28gaW5jbHVkZXMgYSB3YXkgdG8gc2NvcmUgZ3JvdXBzIG9mIGNlbGxzIGJhc2VkIG9uIHRoZWlyIGNlbGwgY3ljbGUgc3RhdGUuIFRoaXMgY2FuIGJlIGhlbHBmdWwgaWYgd2XigJlyZSBmb3IgZXhhbXBsZSBsb29raW5nIGZvciBwcm9saWZlcmF0aW5nIG9yIHN0ZW0gY2VsbCBwb3B1bGF0aW9ucy4KClRoaXMgZnVuY3Rpb24gaXMgYSBzcGVjaWZpYyBjYXNlIG9mIHRoZSBBZGRNb2R1bGVTY29yZSBmdW5jdGlvbiwgd2hpY2ggcXVhbnRpZmllcyB0aGUgcHJlc2VuY2Ugb2YgYW55IGdlbmUgbW9kdWxlIG9mIGNob2ljZS4KYGBge3J9CnNyYXRfZmlsdCA9IENlbGxDeWNsZVNjb3Jpbmcoc3JhdF9maWx0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzLmZlYXR1cmVzID0gY2MuZ2VuZXMkcy5nZW5lcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZzJtLmZlYXR1cmVzID0gY2MuZ2VuZXMkZzJtLmdlbmVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXQuaWRlbnQgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5iaW4gPSAxNSkKCkRpbVBsb3Qoc3JhdF9maWx0LCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gIlBoYXNlIikKYGBgCgpTaW1wbGVyIChhbmQgb2Z0ZW4gbW9yZSBhY2N1cmF0ZSkgd2F5cyBvZiBkb2luZyB0aGlzIG9mdGVuIGNvbnNpc3Qgb24gcGxvdHRpbmcgZXhwcmVzc2lvbiBvZiBjZWxsIGN5Y2xlIGFzc29jaWF0ZWQgZ2VuZXMKYGBge3J9CkZlYXR1cmVQbG90KHNyYXRfZmlsdCwgZmVhdHVyZXMgPSBjKCJUT1AyQSIsICJNS0k2NyIsICJDREsxIiksIG9yZGVyID0gVCkKYGBgCgoKYGBge3J9CiNzYXZlUkRTKHNyYXRfZmlsdCwgZmlsZSA9ICJFeGFtcGxlX2ZpbHRfc3JhdC5SRFMiKQpgYGAKCg==